热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

迭代地生成自然数的排列

如何解决《迭代地生成自然数的排列》经验,为你挑选了2个好方法。

我有一个不寻常的问题,可能会或可能不会被问过(虽然我没有找到任何东西,但我可能只是寻找错误的流行语).

我的任务很简单:给出自然数字的"列表",直到N [0,1,2,... N - 1]我想要改变这个序列.例如,当我输入数字4时,一个可能的结果将是[3,0,1,2].随机性应该由一些种子确定(然而这对于大多数普通语言的PRNG来说是标准的).

天真的方法是实例化一个大小为N的数组,用数字填充它并使用任何改组算法.

然而问题是,这种方法的存储器复杂性是O(n),在我的特殊情况下是不易处理的.我的想法是,编写一个生成器,迭代地在结果列表中提供数字.

更确切地说,我想要一些以迭代方式提供数字的"算法".更确切地说,概念类看起来像这样:

class Generator {
   // some state
   int nextNumber(...) {
      // some magic
   }
}

并且迭代地调用nextNumber方法提供序列的编号(即[0,1,... N-1]的任何排列.当然,这个生成器实例的状态应该比O(n)具有更好的内存复杂性再一次(我什么也得不到).

有什么算法要做,我想要什么?



1> PM 2Ring..:

这是使用我在大约2年前写的平衡Feistel网络的格式保留加密的 Python 3中相当简单的实现.它可以在32位系统上执行N到2 64的索引排列,或在64位构建的Python上执行2 128.这是由于函数返回的整数的大小.请参阅查找系统的限制.使用可以返回更大位长的值的更高级的哈希函数并不难,但我不想让这段代码更复杂或更慢.hash()sys.hash_info

更新

我对之前的版本进行了一些小改进,并在评论中添加了一些更多信息.我们使用高位来代替使用散列函数返回的低位,这通常会改善随机性,特别是对于短位长度.我还添加了另一种散列函数,xxhash由扬科莱,其中工程作品比Python的更好的hash为这个应用程序,特别是对于较短的位长,虽然它是一个慢一点.xxhash算法比内置算法具有更高的雪崩效应hash,因此产生的排列往往更加良好.

尽管此代码适用于较小的值,stop但它更适合处理stop >= 2**16.如果您需要置换较小的范围内它可能是一个好主意,只是使用random.shufflelist(range(stop)).它会更快,并且它不会使用那么多RAM:list(range(2**16))在32位机器上消耗大约1280千字节.

您会注意到我使用字符串为随机数生成器播种.对于这个应用程序,我们希望随机数发生器具有足够的熵,并且使用大字符串(或bytes)是一种简单的方法,正如random模块文档提到的那样.即便如此,这个程序只能在stop很大的时候产生所有可能排列的一小部分.因为stop == 35有35个!(35阶乘)不同的排列,35!> 2 132,但我们的密钥的总位长仅为128,因此它们无法涵盖所有​​这些排列.我们可以增加Feistel轮数以获得更多的覆盖范围,但显然这对于​​大的值来说是不切实际的stop.

''' Format preserving encryption using a Feistel network

    This code is *not* suitable for cryptographic use.

    See https://en.wikipedia.org/wiki/Format-preserving_encryption
    https://en.wikipedia.org/wiki/Feistel_cipher
    http://security.stackexchange.com/questions/211/how-to-securely-hash-passwords

    A Feistel network performs an invertible transformation on its input,
    so each input number produces a unique output number. The netword operates
    on numbers of a fixed bit width, which must be even, i.e., the numbers
    a particular network operates on are in the range(4**k), and it outputs a
    permutation of that range.

    To permute a range of general size we use cycle walking. We set the
    network size to the next higher power of 4, and when we produce a number
    higher than the desired range we simply feed it back into the network,
    looping until we get a number that is in range.

    The worst case is when stop is of the form 4**k + 1, where we need 4
    steps on average to reach a valid n. In the typical case, where stop is
    roughly halfway between 2 powers of 4, we need 2 steps on average.

    Written by PM 2Ring 2016.08.22
'''

from random import Random

# xxhash by Yann Collet. Specialised for a 32 bit number
# See http://fastcompression.blogspot.com/2012/04/selecting-checksum-algorithm.html

def xxhash_num(n, seed):
    n = (374761397 + seed + n * 3266489917) & 0xffffffff
    n = ((n <<17 | n >> 15) * 668265263) & 0xffffffff
    n ^= n >> 15
    n = (n * 2246822519) & 0xffffffff
    n ^= n >> 13
    n = (n * 3266489917) & 0xffffffff
    return n ^ (n >> 16)

class FormatPreserving:
    """ Invertible permutation of integers in range(stop), 0 > self.shiftbits, n & self.lowmask
        for key in keys:
            left, right = right, left ^ (xxhash_num(right, key) >> self.lowshift)
            #left, right = right, left ^ (hash((right, key)) >> self.lowshift) 
        return (right <

产量

Shuffling a small number
0 4 0
1 2 1
2 5 2
3 9 3
4 1 4
5 3 5
6 7 6
7 0 7
8 6 8
9 8 9

Shuffling a small number, with a slightly different keystring
0 9 0
1 8 1
2 3 2
3 5 3
4 2 4
5 6 5
6 1 6
7 4 7
8 7 8
9 0 9

Here are a few values for a large maxn
maxn = 10000000000000000000
0: 7071024217413923554 0
1: 5613634032642823321 1
2: 8934202816202119857 2
3:  296042520195445535 3
4: 5965959309128333970 4
5: 8417353297972226870 5
6: 7501923606289578535 6
7: 1722818114853762596 7
8:  890028846269590060 8
9: 8787953496283620029 9

Using a set to test that there are no collisions...
maxn 100000
True

Testing that the operation is bijective...
yes
0 4
1 2
2 5
3 9
4 1
5 3
6 7
7 0
8 6
9 8

以下是如何使用它来制作发电机:

def ipermute(stop, keystring):
    fpe = FormatPreserving(stop, keystring)
    for i in range(stop):
        yield fpe.fpe(i)

for i, v in enumerate(ipermute(10, 'secret key string')):
    print(i, v)

产量

0 4
1 2
2 5
3 9
4 1
5 3
6 7
7 0
8 6
9 8

它的速度相当快(对于Python),但它绝对适合加密.它可以通过增加扩展Feistel的数量舍入到至少5,并通过使用合适的密码散列函数,例如进行加密级Blake2.此外,需要使用加密方法来生成Feistel密钥.当然,除非你确切知道自己在做什么,否则不应该编写加密软件,因为编写易受时序攻击等影响的代码太容易了.



2> Matt Timmerm..:

你正在寻找的是一个函数形式的伪随机置换,比如f,它以伪随机双射方式将1到N的数字映射到1到N的数字.然后,要以伪随机排列生成第n个数,只需返回f(n)

这基本上与加密问题相同.具有密钥的分组密码是伪随机双射函数.如果按顺序将所有可能的纯文本块精确地输入一次,它将以不同的伪随机顺序返回所有可能的密文块.

因此,为了解决像你这样的问题,你基本上做的是创建一个密码,它可以处理从1到N而不是256位块或其他数字的数字.您可以使用加密工具来执行此操作.

例如,您可以使用Feistel结构(https://en.wikipedia.org/wiki/Feistel_cipher)构建您的置换函数,如下所示:

    设W为floor(sqrt(N)),并将函数的输入设为x

    如果x

    x =(x +(NW ^ 2))%N

    重复步骤(2)和(3)若干次.你做得越多,结果越随机.步骤(3)确保x

由于该函数由许多步骤组成,每个步骤将以0和N-1的数字以双射方式将数字从0映射到N-1,整个函数也将具有此属性.如果你输入从0到N-1的数字,你将以伪随机顺序将它们退回.


推荐阅读
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 本文介绍了Python语言程序设计中文件和数据格式化的操作,包括使用np.savetext保存文本文件,对文本文件和二进制文件进行统一的操作步骤,以及使用Numpy模块进行数据可视化编程的指南。同时还提供了一些关于Python的测试题。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • 本文介绍了在C#中SByte类型的GetHashCode方法,该方法用于获取当前SByte实例的HashCode。给出了该方法的语法和返回值,并提供了一个示例程序演示了该方法的使用。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 流数据流和IO流的使用及应用
    本文介绍了流数据流和IO流的基本概念和用法,包括输入流、输出流、字节流、字符流、缓冲区等。同时还介绍了异常处理和常用的流类,如FileReader、FileWriter、FileInputStream、FileOutputStream、OutputStreamWriter、InputStreamReader、BufferedReader、BufferedWriter等。此外,还介绍了系统流和标准流的使用。 ... [详细]
  • 本文介绍了如何使用go语言实现一个一对一的聊天服务器和客户端,包括服务器开启、等待客户端连接、关闭连接等操作。同时提供了一个相关的多人聊天的链接供参考。 ... [详细]
author-avatar
小甜蜜陈诗蓉_614
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有