【问题标题】:Calculating Josephus Permutations efficiently in Javascript在 Javascript 中有效地计算 Josephus 排列
【发布时间】:2019-11-05 13:24:05
【问题描述】:

在训练代码战时,我遇到了关于约瑟夫排列的挑战,我尝试先在纸上解决它,然后再将其转换为代码。

问题如下: “创建一个返回 Josephus 排列的函数,将要排列的项目的初始数组/列表作为参数,就好像它们在一个圆圈中一样,并计算每 k 个位置,直到没有剩余。”

我的主要想法是:

  • 有一个辅助数组来保持响应
  • 使用两个迭代器:

    • i:跟踪给定数组中的当前索引
    • k:跟踪排列的步骤
  • 在 0 处初始化 i,在 1 处初始化 k

  • 当原始数组只剩下一个元素时:
    • 将元素推送到输出数组
  • 只要 i 不是数组的最后一个索引:
    • 如果 k = 步长:
      • 从原数组中取出元素,推入输出数组,最后替换k = 1
    • 如果 k != 步骤:
      • 增加 i 和 k
  • 当 i 是原始数组的最后一个索引时(并且该数组有多个元素):
    • 如果 k = 步长:
      • 从原数组中取出元素,压入输出数组,替换k = 1,设置i = 0
    • 如果 k != 步骤:
      • 设置 i = 0 并递增 k
function josephus(items,step){
  var output = [];
  var i = 0;
  var k = 1;
  if( items == [] ) {
    return [];
  }
  while (items.length != 1) {
    if        (k == step && i == items.length - 1) {
      output.push(items[i]); 
      items.splice(i, 1);
      i = 0;
      k = 1;
    } else if (k == step && i != items.length - 1) {
      output.push(items[i]);
      items.splice(i, 1);
      k = 1
    } else if (k < step && i == items.length - 1) {
      k++;
      i=0;
    } else if (k < step && i != items.length - 1) {
      k++;
      i++;
    }
  }
  output.push(items[0]);
  return output;
}

这可行,但效率不高,当我在运行示例测试中运行它时,我得到 5 个示例测试已通过,但它还包括一个 STDERR:执行超时(12000 毫秒)。

样本测试如下:

Test.assertSimilar(josephus([1,2,3,4,5,6,7,8,9,10],1),[1,2,3,4,5,6,7,8,9,10])
Test.assertSimilar(josephus([1,2,3,4,5,6,7,8,9,10],2),[2, 4, 6, 8, 10, 3, 7, 1, 9, 5])
Test.assertSimilar(josephus(["C","o","d","e","W","a","r","s"],4),['e', 's', 'W', 'o', 'C', 'd', 'r', 'a'])
Test.assertSimilar(josephus([1,2,3,4,5,6,7],3),[3, 6, 2, 7, 5, 1, 4])
Test.assertSimilar(josephus([],3),[])

我的问题是,我怎样才能提高效率?

是我使用的算法错误还是实现?

一条评论提到了两件事:

  • push() 很慢,这是我的一种可能(错误的数据结构)

  • 建议查看递归(这更让我怀疑算法)。不过,我并没有真正了解如何进行递归。

提前感谢您的帮助!

【问题讨论】:

  • 不知道codereview,会去看看。我正在寻找替代方案,但没有想到递归。
  • splice 往往很昂贵,但我无法想象您提供的测试可能会持续 12 秒,除非您的代码中的某些循环卡住或设置为在非凡数量后完成重复次数。
  • @גלעד ברקן:如果我没记错的话,CodeWars 为您提供了一些示例测试用例,以便您测试算法的正确性,但隐藏了其他用于确保运行时效率的示例。

标签: javascript arrays algorithm permutation josephus


【解决方案1】:

有一个重复,可以记住。 (这似乎通过了 Codewars 测试。)

function g(n, k, i, memo){
  if (memo.hasOwnProperty([n, k, i]))
    return memo[[n, k, i]];
    
  if (i == 1)
    return memo[[n, k, i]] = (k - 1) % n;
    
  return memo[[n, k, i]] =
    (k + g(n - 1, k, i - 1, memo)) % n; 
}

function f(A, k){
  let n = A.length;
  let result = new Array(n);
  let memo = {};
  
  for (let i=1; i<=n; i++)
    result[i - 1] = A[ g(n, k, i, memo) ];
  
  return result;
}

let str = '';

str +=  JSON.stringify(f([1,2,3,4,5,6,7,8,9,10],1)) + '\n';
//[1,2,3,4,5,6,7,8,9,10])

str += JSON.stringify(f([1,2,3,4,5,6,7,8,9,10],2)) + '\n';
//[2, 4, 6, 8, 10, 3, 7, 1, 9, 5])

str += JSON.stringify(f(["C","o","d","e","W","a","r","s"],4)) + '\n';
//,['e', 's', 'W', 'o', 'C', 'd', 'r', 'a'])

str += JSON.stringify(f([1,2,3,4,5,6,7],3)) + '\n';
//,[3, 6, 2, 7, 5, 1, 4])

str += JSON.stringify(f([],3))
//,[])

console.log(str);

为了解释重复,删除的第一个元素(i = 1 时)显然是 (k - 1) mod n(零索引)。现在考虑查找g(n, k, i)。第一个被删除的元素是(k - 1) mod n,然后我们从kth 位置开始。所以问题是在删除(k - 1) mod n处的元素并从k开始后找到删除的(i - 1)th元素,即(k + g(n - 1, k, i - 1)) mod n

【讨论】:

【解决方案2】:

您可以将前导位移到末尾。

const josephus = (x) => parseInt(x.toString(2).substr(1) + 1, 2);

【讨论】:

    【解决方案3】:

    您是否尝试过实现函数式方法? 来自wikipedia

    function getSafePosition(n) {
      // find value of L for the equation
      valueOfL = n - highestOneBit(n);
      safePosition = 2 * valueOfL + 1;
    
      return safePosition;
    }
    
    function highestOneBit(i) {
      i |= (i >> 1);
      i |= (i >> 2);
      i |= (i >> 4);
      i |= (i >> 8);
      i |= (i >> 16);
      return i - (i >> 1);
    }
    

    这应该在 O(n) 中运行

    【讨论】:

    • 我可能计算错了我的大 O,但我的代码不应该围绕 O(step * n) = O(n) 运行吗?将检查该解决方案!
    • OP 有step 作为参数,它并不总是2
    猜你喜欢
    • 2011-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多