问题 循环数字


所以这是给出的问题。

你在一个有100个椅子的房间里。椅子按顺序从1到100编号。

在某个时间点,#1椅子上的人将被要求离开。将跳过椅子#2中的人,并且将要求椅子#3中的人离开。这种跳过一个人并要求下一个人离开的模式将继续绕着圆圈走,直到有一个人离开,幸存者。

这就是我提出的答案。我相信这是正确的答案,我在纸上做了大约10次,每次都得到74。 这是一个特技问题吗?因为我不知道该怎么做。

这是jsfiddle http://jsfiddle.net/cQUaH/

var console = {
    log : function(s) {
        document.body.innerHTML += s + "<br>";
    }
};

var chairArr = [];
for (var i = 1; i <= 100; i++){
    chairArr.push(i);
}

var j = 2;
while(chairArr.length > 1) {
    console.log('removing ' + chairArr[j]);
    chairArr.splice(j, 1);
    j++;
    if(j >= chairArr.length) {
       console.log('--- Finished pass');
       console.log('--- Array state:');
       console.log(chairArr);
       j = (j == chairArr.length) ? 0 : 1;   
    } 
}
console.log('--- Final result: ' + chairArr); 
//result 74

6628
2017-09-07 01:50


起源

老师说答案应该是什么?当问题说#1应该先出现时,你为什么要先从#3椅子上移走这个人? - nnnnnn
为什么 j 从2开始? - Matt Greer
这是我收到的回复。它看起来很稳固但不幸的是你只是勉强不合时宜。再看看,看看你是否看到了错误。 - user2677350
你说“主席#1中的人将被要求离开”。第一次通过你不是删除#1所以你需要将变量j调整为零。 - jeff
正如你所说的那样,问题是#1号椅子上的人会先离开,但你从#3开始。为什么你认为这是一个技巧问题? - nnnnnn


答案:


随着指数的微小变化,你有了 约瑟夫斯问题。在传统的公式中,人1杀人2,3杀4等。要转换为该形式,杀掉人1,如你的问题所述,然后通过减去1给人2-100,给人1-99。

约瑟夫斯问题的一个很好的处理方法,包括它在70-73 CE的犹太人起义中的起源,是在 具体数学,第2版,Graham,Knuth和Patashnik,第1.3节。维基百科和Wolfram MathWorld都有关于这个问题的文章,维基百科甚至包括约瑟夫斯的原始描述 犹太战争

本书为解决方案提供了一个稍微复杂的递归,以及一个更简单的算法。如果人数是 n,和 n = 2^l + m 哪里 l 尽可能大,那么答案就是 2m+1。所以,从那以后 99 = 2^6 + 35,解决方案是 2*35 + 1 = 71。但是你需要反转重新编号,所以真正的答案是 72

但是,就您的编程问题而言,为什么不将其作为基本操作 移除圈中的第一个人并将第二个人移到最后。  所以,随着 5 人, [1,2,3,4,5],你删除第一个获得 [2,3,4,5]并将新的第一个元素移动到最后 [3,4,5,2]

var killAndRotate = function(array) { // say [1,2,3,4,5]
    var dead    = array.shift(),      // dead    = 1, array = [2,3,4,5]
        skipped = array.shift();      // skipped = 2, array = [3,4,5]
    array.push(skipped);              //              array = [3,4,5,2]
}

然后主循环变为:

while (chairArray.length > 1) {
    killAndRotate(chairArray);
}
alert(chairArray[0]); // or console.log, or return.
// In turn, array is:
// [1,2,3,4,5]
// [3,4,5,2]
// [5,2,4]
// [4,2]
// [2] and we alert, log, or return 2.

添加

找到原始Josephus问题的结果的简单方法是看到:

如果有 2^l 人们,然后在第一关中所有偶数人都被杀死,所以第一个人还活着。

1 2 3 4 5 6 7 8

  X   X   X   X

现在有 2^(l - 1) 人。再次,第一个人幸存下来:

1 2 3 4 5 6 7 8

  X   X   X   X

    X       X

重复这个过程;第一个人每次通过幸存者,最后一个幸存者也是如此。

现在,假设有 m 额外的人 m < 2^l。这里, l = 3 和 m = 5。杀了第一个 m 人要死。

1 2 3 4 5 6 7 8 9 10 11 12 13

  X   X   X   X    X  Y

现在,有 2^l 人们离开了,人 2 m + 1 = 11 是排在第一位的。所以他活了下来。

还应该指出,添加新的索引变量和拼接可能会导致程序员错误。由于您只需要从前面移除并添加到后面,因此请使用数组的基本方法。


6
2017-09-07 02:28



“如果人数是n,n = 2 ^ l + m,其中l尽可能大,那么答案是2m + 1。”数学太美了! - Joseph Myers


在我看来答案是72.当你意识到,而不是删除数字,你可以跳过它们,代码变得非常简短和直截了当。

var chairArr = [];
for (var i = 1; i <= 100; i++)
    chairArr.push(i);

for (i = 1; i < chairArr.length-2; i = i + 2)
    chairArr.push(chairArr[i]);

console.log('--- Final result: ' + chairArr[i]);

3
2017-09-07 02:44



这个答案背后有天才,我认为没有人注意到。这个答案正在做的是将幸存者添加到数组的末尾。但尽管如此,阵列索引很快就能赶上最后的幸存者。这个解决方案很漂亮,甚至可以指出你的索引排列后循环值 i 完全让孤独的幸存者进入 chainArr[i]。 - Joseph Myers


你在这里描述的是Josephus问题,可以使用动态编程解决:

function josephus(n, k) 
{ 
    if (n == 1) { 
        return 1; 
    } else { 
        return ((josephus(n-1, k) + k - 1) % n) + 1; 
    }
}

alert(josephus(100, 2));

资源: 维基百科

n 表示椅子的数量和 k 表示每个第k个人离开。

结果是73。

更新

不幸的是,我没有正确地阅读问题。上面的代码解决了一个稍微不同的问题;第二人不是杀掉第一轮中的第一人,而是杀死第二人。成为幸存者取决于细节:)

解决您的代码问题非常简单,首先是第一个人而不是第一个人中的第三个人。

var chairArr = [];

for (var i = 1; i <= 100; i++){
    chairArr.push(i);
}

var j = 0;
while (chairArr.length > 1) {
    chairArr.splice(j, 1);
    j = (j + 1) % n;
}

3
2017-09-07 02:25



我不明白为什么它是73而不是72.我读了维基文章并且没有得到它。当我在纸上做,并摆脱1,3,5等等,我得到72.请向我解释为什么这是答案。 - user2677350
你是说任何机会的递归吗? - Itay Moav -Malimovka
约瑟夫斯问题删除偶数,而不是奇数。 - DiMono
@ user2677350我现在必须运行,但我会回来解释:)它可能与最初被跳过的第一个有关。 - Ja͢ck
@ ItayMoav-Malimovka动态编程和递归不是互斥的,我希望你意识到:) - Ja͢ck


您不需要迭代来查找结果,有一个公式可用于获取最终的主席:

function findChair (input) {
  return (input - Math.pow(2, Math.floor(Math.log2(input)))) * 2 || (input === 1 ? 0 : input)
}

而对于原来的约瑟夫斯问题,你可以简化偶数,公式可以简化:

function findChair (input) {
  return (input - Math.pow(2, Math.floor(Math.log2(input)))) * 2 + 1
}

关于原始问题的一个很酷的事情是你可以使用二进制文件。例如:

100 = 1100100

取第一个'1'并将它放到最后:

1001001 = 73


0
2017-09-11 14:02