【问题标题】:Permuting n elements by swapping each element by no more than k positions通过将每个元素交换不超过 k 个位置来置换 n 个元素
【发布时间】:2025-12-27 09:35:15
【问题描述】:

我拥有的是一个向量(示例中为 n = 4):

x = '0123';

我想要的是一个与 x 大小相同且具有与 x 中相同元素但顺序不同的向量 y:

y = ['0123'; '0132'; '0213'; '0231'; '0312'; '0321'; '1023'; '1032'; '1203'; '1302'; '2013'; '2031'; '2103'; '2301'];
y(ceil(rand * numel(y(:, 1))), :)

即一个排列,使得 y 中的每个元素相对于其在 x 中的原始位置(在示例中为 k = 2)随机更改不超过 k 个位置。概率分布必须是均匀的(即每个排列必须同样可能发生)。

一种明显但低效的方法当然是找到一个随机的无约束排列,并事后检查这是否符合约束。对于小向量,您可以找到所有排列,删除那些不允许的排列,然后在剩余的排列中随机选择。 关于如何更有效地执行相同操作的任何想法,例如通过实际交换元素?

【问题讨论】:

    标签: matlab permutation constraint-programming


    【解决方案1】:

    使用约束编程可以轻松生成所有排列。这是上面示例使用MiniZinc 的简短模型(请注意,我们假设 x 将包含 n 个不同的值):

    include "globals.mzn";
    
    int: k = 2;
    int: n = 4;
    array[1..n] of int: x = [0, 1, 2, 3];
    array[1..n] of var int: y;
    
    constraint forall(i in 1..n) (
        y[i] in {x[i + offset] | offset in -min(k, i-1)..min(k, n-i)}
      );
    
    constraint all_different(y);
    
    solve :: int_search(y, input_order, indomain_min, complete)
      satisfy;
    
    output [show(y)];
    

    在大多数情况下,约束规划系统可以使用随机搜索。但是,这不会为您提供结果的均匀分布。然而,使用 CP 将比简单方法(生成并测试有效性)更有效地生成所有有效排列。

    如果您需要有效地生成随机排列,我认为可以修改标准Fisher-Yates shuffle 以直接处理它。标准算法使用数组的其余部分从中选择下一个值,并选择具有均匀概率分布的值。应该可以只保留当前有效选择的列表,并更改值的概率分布以匹配所需的输出。

    【讨论】:

      【解决方案2】:

      除了你提到的拒绝方法,我没有看到任何其他方法。但是,与其列出所有允许的排列然后选择一个,不如避免该列表更有效。因此,您可以随机生成一个排列,检查它是否有效,如果不是则重复:

      x = '0123';
      k = 2;
      
      n = numel(x);
      done = 0;
      while ~done
          perm = randperm(n);
          done = all( abs(perm-(1:n)) <= k ); %// check condition
      end
      y = x(perm);
      

      【讨论】:

      • 谢谢 Luis,目前看来这是最好的方法。如果n 很小,则生成允许的排列列表可能会更快。
      • 欢迎您!此外,如果您打算生成许多排列,生成整个列表可能会更好