【问题标题】:Why `Loop` write more than once为什么`Loop`写不止一次
【发布时间】:2017-03-14 15:35:19
【问题描述】:

我尝试用javascript 创建一个guessed,这是代码:

<script>
function makeid(len)
{
    var text = "";
    //var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    var possible = "abc";

    for( var i=0; i < len; i++ )
        text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
}
 ////////////////////////////////////////////




var password = 'abc';
var correctGuess = false
var guess; 

do {
    document.write(makeid(3) + "<br>");
    guess = makeid(3);
  if (guess === password) {
    correctGuess = true;
  }
} while ( ! correctGuess ) 
  document.write("You know the secret password. Welcome.");

</script>

但不幸的是,结果重复了不止一次: 结果:

abb baa aac cba cbb aba bbb aac acb cba ccc bab caa bab ccc aac ccb aba abc bac cbb

这会拖慢程序,如何解散这个问题 有解决办法吗? 谢谢

【问题讨论】:

  • document.write 行中的makeid 与猜测不符...
  • 我已经发布了答案,请随时查看。

标签: javascript loops for-loop foreach


【解决方案1】:

由于您不想两次检查相同的密码,因此生成随机猜测显然不是正确的方法。正如 klumme 所提到的,存储一系列先前的猜测只会增加时间和空间的复杂性,所以这也被淘汰了。您将要做的是使用蛮力方法,即尝试每种字符组合,直到得到正确答案。以下是如何实现它:

注意:请记住,暴力破解算法通常效率很低,如果您使用原始代码中的完整字母数字字符串暴力破解超过 3-4 个字符的密码,这将花费大量时间(尤其是在浏览器中)。 JavaScript 本质上不是一种非常强大的数字运算语言 - 所以这个答案更多的是为了它的想法,而不是在大多数现实世界的环境中使用。

function guesser(len) {
  var arr = Array.apply(null, Array(len));
  var propIndex = -1;
  var indexes = arr.reduce(function(total, curr) {
    propIndex++;
    total[propIndex] = 0;
    return total;
  }, {});
  var lastGuess = arr.map(function() {
    return possible[possible.length - 1];
  }).join("");
  var guess = "";
  var found = false;
  while (guess !== lastGuess) {
    guess = "";
    for (var i = 0; i < propIndex; i++) {
      // if on last char, reset to 0 and increment previous index start position
      if (indexes[propIndex - i] >= possible.length) {
        indexes[propIndex - i - 1]++;
        indexes[propIndex - i] = 0;
      }
    }
    for (var i in indexes) {
      guess += possible[indexes[i]];
    }

    document.write(guess + "<br/>");
    if (guess === password) {
      found = true;
      break;
    }
    // increment last char
    indexes[propIndex]++;
  }
  if (found) {
    document.write("You know the secret password. Welcome.");
  } else {
    document.write("Sorry, you do not know the secret password.");
  }
}


var password = 'dcd';
var possible = "abcd";
guesser(password.length);

【讨论】:

  • 你知道吗,你很棒 :) .. 感谢你和 klummekind user 的尝试
  • 好吧对不起兄弟,关于注意的另一件事.. 有没有简单的浏览器来处理这些事情on example
  • @عبدالرحمنالذهبي 我不明白你的问题 - 你能改写一下吗?
  • 我的意思是,如果浏览器中有超过 4 个字符,解决方案会停止。你有什么想法吗?
  • It's not just a matter of licensing. 我们要求发帖者理解代码并能够解释其中的设计决策。此外,我们不能出于道德原因批评其他人的代码。
【解决方案2】:

如果我理解正确,问题是随机密码功能(“makeid”)可能会多次返回相同的密码。这并不奇怪,该功能没有理由知道已经尝试过哪些密码。您可以跟踪已经尝试过的密码,如果之前尝试过密码,则不要尝试(如 Kind 用户的回答),但在这种情况下,它可能不会加速程序。

更好的方法可能是系统地而不是随机地迭代可能的密码。例如,先尝试“aaa”,然后尝试“aab”、“aac”、“aba”等​​。

这是我想出的——它可能不是很快。在实际尝试密码之前,我在“可能”字符串中使用了一组索引,因为我不想在途中弄乱 indexOf()。

const correctPassword = 'abc';
const possible = 'abc';
const maxIndex = possible.length - 1;

function next(previous) {
    var i = previous.length - 1;
    while (previous[i] === maxIndex) {
        previous[i] = 0;
        i--;
        // All passwords have been tried.
        if (i < 0) {
            return [];
        }
    }
    previous[i]++;
    return previous;
}

var current = Array(3).fill(0);
var currentPassword;

while (current.length != 0) {
    currentPassword = current.map(function (i) {
        return possible[i];
    }).join('');
    document.write(currentPassword + '<br>');
    if (currentPassword === correctPassword) {
        document.write('You know the secret password. Welcome.');
        break;
    }
    current = next(current);
}

【讨论】:

  • 你有完全正确的想法,把它写进代码,我会赞成
  • 如果你能提供一个算法来解决你写的问题,我会支持你的答案两次。
  • 好主意,你能以编程方式解释这个想法吗?
【解决方案3】:

首先,将结果存储在一个数组中。其次,添加以下条件:if (arr.indexOf(guess) == -1) - 如果猜测的数字已经在数组中 - 跳过它。

function makeid(len) {
  var text = "";
  //var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  var possible = "abc";

  for (var i = 0; i < len; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }
  return text;
}

var password = 'abc';
var correctGuess = false;
var guess;
var arr = [];

while (!correctGuess) {
  guess = makeid(3);
  if (arr.indexOf(guess) == -1) {
    arr.push(guess)
    if (guess === password) {
      correctGuess = true;
    }
  }
}
console.log(arr);

【讨论】:

  • OP 要求删除重复的密码猜测。例如,他们希望防止多次检查“aab”。
  • @mhodges 我正在编辑我的帖子,而你已经写好了,伙计。
  • 这不会使算法更有效..它仍然会生成重复值并且仍然对重复值进行比较。事实上,检查indexOf 实际上可能比直接与密码进行比较要慢得多。此外,存储先前猜测的数组会占用内存,如果可能的字符只有a, b, c,这不是一个大问题,但是当您使用字母数字时,您的猜测数组可能会使浏览器内存不足。
  • @mhodges 我知道谈话和投票很便宜,但如果你在乎,请提供更有效的方法来解决它。
  • 否决按钮上的工具提示显示“这个答案没有用”。您的答案正是如此——它比 OP 的原始代码效率低且内存密集。我正在研究解决方案,但这需要时间。我不想随意发布无用的答案。
【解决方案4】:

我对这个问题很感兴趣,并决定借此机会了解更多关于生成器的信息。注意:使用 ES6 语法,因此不一定兼容所有平台。

相对于已经采用的其他方法,我不一定会推荐此方法,但它可能是一个很好的未来参考。

/**
 * Invoke `callback` with every possible combination of `elements` up to length of `length`, until `callback` returns `true`
 * @param elements an array of elements to be passed to `callback`
 * @param length the maximum number of elements to pass to `callback`
 * @param callback a function taking an array of elements, that returns a boolean
 * @returns the first combination of elements for which `callback(combination)` returns `true`. Returns undefined if no combination up to the specified `length` returns `true`.
 */
const combineAndCall = (elements = [], length = 0, callback) => {
  const it = permuteIterator(elements, length);

  for (const el of it) {
    if (callback(el)) {
      return el;
    }
  }
};

/**
 * Returns a generator that returns permutations, with repeated elements, of an array. The maximum length of each permutation is `len`
 * @param arr the array to iterate. The first iteration will always be the empty array.
 * 
 * Example:
 * const it = permuteIterator([1,2,3], 2);
 * it.next().value; // []
 * it.next().value; // [1]
 * it.next().value; // [2]
 * it.next().value; // [3]
 * it.next().value; // [1,1]
 * it.next().value; // [1,2]
 * it.next().value; // [1,3]
 * it.next().value; // [2,1]
 * it.next().value; // [2,2]
 * ...
 * it.next().value; // [3,3]
 * 
 * @len the maximum length of each permutation
 * @returns a generator that iterates the array
 */
function *permuteIterator(arr, len) {
  let current = [];

  function *helper(current, arr, len) {
    if (current.length >= len) {
      yield current;
    } else {
      for (const el of arr) {
        yield* helper([...current, el], arr, len);
      }
    }
  }

  for (let i = 0; i <= len; i++) {
    yield* helper([], arr, i);
  }
}

/**
 * Validates a password
 * @param elements an array of strings (usually single characters) to combine into a a single string, and compare against the password
 * @returns true if the string resulting from `elements.join("")` exactly equals the real password, false otherwise
 */
const passwordValidator = (elements) => {
  const guess = elements.join("");
  //console.log("validating:", guess);
  return guess === "abc";
};

const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
//const alphabet = "abc";
const elements = alphabet.split("");
const guessedPassword = combineAndCall(elements, 3, passwordValidator);

if (guessedPassword) {
  console.log(`You know the secret password '${guessedPassword.join("")}'. Welcome.`);
} else {
  console.log("You don't know the secret password. Rejected.");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-26
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多