【问题标题】:How do I make a permutation function that accepts one value at a time?如何制作一次接受一个值的置换函数?
【发布时间】:2013-05-07 09:56:45
【问题描述】:

我正在寻找一个函数,它接受区间 0,1....N 中的 1 个数字并返回同一区间中的置换值。

0、1、2、3、4、5 和 f(x) 的示例如下:

f(0)=5;
f(1)=1;
f(2)=0;
f(3)=4;
f(4)=2;
f(5)=3;

根据我的研究/理解,这是一个循环群,其中 f(x) 是它的全部基础。 如果发现函数 f(x) = 911 * x % N 是我想要的示例,但是,使用它时会出现模式。 911 是一个很大的素数,通过改变它,我得到了另一个排列,但仍然出现了模式。我希望结果是随机的。

我知道我可以使用 [ 0, 1, 2...N ].shuffle() 之类的东西预先生成排列,但就我而言,我不能这样做。

我有一项服务,它接受输入一个数值并返回置换后的值/位置。 N 设置在服务器端,我正在寻找的功能也是如此。

【问题讨论】:

  • 您想要一个随机顺序为 X 个值的数组吗?
  • 我可能很笨,但我不确定你在这里所说的“排列”是什么意思。通常,排列意味着重新排列给定的项目。就像,(2,3,5)的排列可能是(5,3,2)。你给你的函数一个数字,它返回一个数字。你到底想“置换”什么?
  • 函数接受 1 个数字 =x,发生数学运算,返回 1 个数字,它是一个随机位置,并且在 x 来自的同一区间内是唯一的。
  • @user1916182,请参阅我在问题中的示例。
  • 说清楚 - 所以你只是想从一组数字到另一组数字的双射,它们在每组中的位置不同?

标签: math


【解决方案1】:

请记住,我在这里描述的算法是基于列表 [1, 2, ... N-1](长度为 N-1)。如果您坚持使用列表 [0, 1, ..., N](长度为 N+1),请应用所需的小修改。此外,为简洁起见,我使用 % 操作数的方式与大多数编程语言略有不同:a % b 取值介于 1 和 b 之间,而不是介于 0 和 b - 1 之间,但背后的主要思想当然没有改变,所以a % b 的值是1到b之间的整数,与a一致,取模b。

如果您仔细阅读本文,您会很明显,生成的 shuffle 根本不是随机的。然而,如果参数选择得当,模式将不容易识别,(我的模幂运算的基本思想来自密码学,其中具有不可识别的模式和不可恢复的函数很重要)。

这更像是对算法的语言无关描述,而不是实际的编程解决方案。我不会详细介绍您可能遇到的有效实现和陷阱。我希望它仍然有帮助。我还在 python 中编写了其中的一些部分,因此我可以提供进一步的帮助,甚至在需要时分享我的代码,但这需要一些完成和重构。

使用求幂而不是乘法来消除模式

您对 f(x) = t * x % N(您选择 t 为 911)的初步试验显示了一些模式,因为乘法保持线性(在它的“模块化”含义中)。

如果您使用幂运算而不是乘法运算,您可以获得更多随机的感觉。像 f(x) = t ^ x % N 之类的东西。但是,必须明智地选择 t(就像在乘法的情况下一样,成为 N 的互质数),并且这个公式给出的输出不会提供不同的仅在 N 为素数的情况下,不同 x 值的数字。

大学水平的数学即将到来,请耐心等待,我会尽量保持简单。

我们需要使用原根。相关的Wikipedia article 可能有很大帮助,但基本思想是精心选择的基数的余数取 1 到 N-1 之间的所有值。例如

3^1 = 3
3^2 = 9 = 2 (mod 7)
3^3 = 27 = 6 (mod 7)
3^4 = 81 = 4 (mod 7)
3^5 = 243 = 5 (mod 7)
3^6 = 729 = 1 (mod 7)

都是不同的(从这一点开始,值从头开始重复:3^7 = 3^1 (mod 7)、3^8 = 3^2 (mod 7),依此类推)。

所以,如果你的 N 是 7,那么 3 就可以成为 t。对于介于 1 和 6 之间的 x 值,您可以使用 f(x) = (3 ^ x) % 7。

这导致以下 f:

f(1) = 3
f(2) = 2
f(3) = 6
f(4) = 4
f(5) = 5
f(6) = 1

引入移位,提供一些额外的随机效果

如果你稍微玩一下,你会注意到,N-1 总是被洗牌为 1。如果你想避免这种情况,我们可以更进一步,选择任意数字 k 加在后面取幂。也就是说,使用 g(x) = (f(x) + k) % (N-1) = ((t ^ x) % N + k) % (N-1),在我们的示例中让 k 为 2,导致排列:

g(1) = 5
g(2) = 4
g(3) = 2
g(4) = 6
g(5) = 1
g(6) = 3

如何选择基地

现在你得到了基本的感觉。但是这个一般怎么用,N不是7的时候呢?

问题的关键在于选择参数 t,在我们的示例中为 3。

不幸的是,这通常是一个难题(数学家称之为finding a primitive root),而且我知道没有任何易于解释、开箱即用的解决方案。

但这只是问题的一部分。更可悲的是,如果 N 是合数,则原根甚至都不起作用。例如,如果 N=6,则表达式 t^x 以 6 取 1 到 5 之间的所有值都不存在任何数字 t。

但这部分解决起来并不难。

如何处理复合 N

如果 N 是合数,我们应该找到一个稍大一点的素数 P,并在基于该算法的算法的基础上,通过用它们的洗牌后值替换超出范围的数字(并迭代,如果需要)。

例如,如果 N 为 6,我们可以选择 P 为 7,并使用我们之前构建的 g(x)。

g(1) = 5 ok (5<=N-1 holds)                          h(1) = 5
g(2) = 4 ok                                         h(2) = 4
g(3) = 2 ok                                    =>   h(3) = 2
g(4) = 6 too large, using g(g(4)) = g(6) = 3        h(4) = 3
g(5) = 1 ok                                         h(5) = 1

为了安全起见,我再举一个 N=4 的例子,我们使用之前计算的 P=7 的解。

g(1) = 5, g(5) = 1                                  h(1) = 1
g(2) = 4, g(4) = 6, g(6) = 3                   =>   h(2) = 3
g(3) = 2                                            h(3) = 2

现在应该很清楚了。选择接近 N 的 P 是明智的,因此 g 的越界值不需要太多的重新计算。

回到寻找 t

所以我们剩下的唯一问题是找到可以用作求幂基础的原根。

如果我之前链接的页面上的数学引起了一些内心的厌恶,我有一些好消息要告诉你:t 的可能好的值在区间 [2, N-1] 中是密集的,所以即使是随机猜测也可能会有所帮助。

在链接页面上有一些详细信息如何有效地验证随机选择的 t 是否真的对我们有好处,但除非您使用非常大的数字,否则您可以进行取幂并检查数字 1 是否出现得更早比 t 的 (N-1) 次方(也许你记得我注意到 t^x=1 (mod N) 在 x=N-1 的情况下始终成立,因此 1 的早期出现会破坏唯一性)。

我建议在 N/2 附近寻找合适的 t(意味着数量级 - 对于 P=91367,t=54949 效果很好)。如果您选择 t 太低(例如 t=2),您可以很容易地发现一些相邻 x 值(2+k, 4+k, 8+k, ... 会出现在彼此)。如果 t 太接近 N,如果在相同奇偶性的连续 x 值中查看 f(x),可能会出现类似的现象。一个好的 t 选择应该涵盖这些模式,并以足够随机的结果结束。

总结

再一次,这里是算法的步骤

(给定N个)

找到一个比 N 稍大的 P 素数

在 1 和 P-1 之间选择任意数字 k

找到 P 的原根 t

(对于给定的 x,输出 shuffle h(x) 是)

计算

f(x) = (t ^ x) % P

计算

g(x) = (f(x) + k) % (P-1)

计算

h(x) = g(x)                                       if g(x)<=N-1,
       iterate the calculations with x = g(x)     otherwise

【讨论】:

  • 我不会通过快速阅读来假装已经 100% 理解它,但我非常期待明天用更多的咖啡和更少的睡眠剥夺来重新审视它。辛苦了,+1!
  • 感谢@Adrian,这个问题真的很有挑战性,所以我放弃了一些任务,试一试。有可能,我的解决方案中有一些模式,根据问题的作者仍然应该避免,但我认为这是正确的。但是,我现在也很累,所以你可能会在我的启发式中发现一些错误。如果是这种情况,请不要犹豫,告诉我们,以便我们更正它并提出最终解决方案。
  • 这看起来棒极了!我现在正在工作,以后会好好看看,但据我所知,这就是我寻求的答案。干杯!
  • 我期待在您仔细查看后听到您的意见。如果对实施细节有任何疑问,请随时与我联系。
【解决方案2】:

这个非常相关的问题 (How to generate a predictable shuffling of a sequence without generating the whole sequence in advance?) 及其接受的答案(以及其中链接的博客文章及其参考论文)可能也很有趣,应该可以解决您的问题。

【讨论】:

    【解决方案3】:

    根据您的评论:

    Function accepts 1 number =x, math happens, 1 number is returned which is a random position AND UNIQUE within the same interval where x comes from
    

    我可能仍然不明白这个问题,但我再试一次。我希望它能给你一些线索/帮助!

    <?php
    $control = array();
    
    $f = array(5,1,0,4,2,3); //Example
    
    $x = 5; //Function accepts 1 number =x
    
    
    for($n=1;$n<41;$n++) {
        echo doTheMath($control, $f, $x); //Echo just for example
    }
    
    function doTheMath(Array &$control, Array &$f, $x) {
        $n = count($f); //Interval count
    
        //1 number is returned 
        //AND
        //UNIQUE within the same interval where x comes from.
    
        while (!in_array($random = array_rand($f, 1), $control)) { //Get another random value from array-element if it's not in control-array
    
            if (!in_array($random, $control)) {
                $control[] = $random;
    
                //The are no more unique values, stop executing php
                if (count($control) == $n) {
                    exit;
                }
    
                $result = 911 * $random * $n;
                return $result . '<hr />';
            }
        }
    
    
        return null;
    
    }
    ?>
    

    示例输出:

    10932
    0
    21864
    27330
    16398
    

    【讨论】:

    • 他说他不能预先洗牌。我想我现在理解他了,他想要一个动态计算唯一“哈希”位置的函数,其中数字的哈希值具有相同的范围。换句话说:函数的范围是给定函数域的排列
    • @user1916182 知道了,我想?我的示例中的函数完成了这项工作,但就像我说的那样,随机素数使输出具有模式。在图表中,它们大多看起来像鱼翅。如果可能的话,我想要更多的随机输出。如果我可以添加一个种子参数来控制随机性/散列/排列的东西,那么额外的布朗尼点:|对不起,我没有像正常人一样表达的适当背景。
    • 它在 javascript 中。变量 N = 6;函数 perm( x ){return 911 * x % N } for( var i=0; i
    • @Discipol - 好的,你只想将你的 JS 代码“转换”为 PHP 吗?
    • 我正在寻找我的问题的答案,而不是代码转换。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-05
    • 2010-10-02
    相关资源
    最近更新 更多