Algorithm 给定一个置换';s的字典编号,是否有可能在O(1)中获得其中的任何项目

Algorithm 给定一个置换';s的字典编号,是否有可能在O(1)中获得其中的任何项目,algorithm,encryption,permutation,combinatorics,number-theory,Algorithm,Encryption,Permutation,Combinatorics,Number Theory,我想知道下面解释的任务在理论上是否可行,如果可能的话,我该如何做 给您一个N元素的空间(即0和N-1之间的所有数字),让我们看看该空间上所有排列的空间,并称之为s。可以标记为S[i]的i的S成员是字典编号i的排列 例如,如果N为3,则S是此排列列表: S[0]: 0, 1, 2 S[1]: 0, 2, 1 S[2]: 1, 0, 2 S[3]: 1, 2, 0 S[4]: 2, 0, 1 S[5]: 2, 1, 0 (当然,当看到一个大的N时,这个空间会变得非常大,N!) 现在,我已经知道了,

我想知道下面解释的任务在理论上是否可行,如果可能的话,我该如何做

给您一个
N
元素的空间(即
0
N-1
之间的所有数字),让我们看看该空间上所有排列的空间,并称之为
s
。可以标记为
S[i]
i
S成员是字典编号
i
的排列

例如,如果
N
为3,则
S
是此排列列表:

S[0]: 0, 1, 2
S[1]: 0, 2, 1
S[2]: 1, 0, 2
S[3]: 1, 2, 0
S[4]: 2, 0, 1
S[5]: 2, 1, 0
(当然,当看到一个大的
N
时,这个空间会变得非常大,
N!

现在,我已经知道了,但我想要更好的

有些排列本身可能是巨大的。例如,如果您正在查看
N=10^20
。(
S
的大小应该是
(10^20)!
,这是我在堆栈溢出问题中提到的最大数字:)

如果你只看空间中的一个随机排列,那么它将非常大,以至于你无法将整个内容存储在硬盘上,更不用说通过字典数字计算每个项目了。我想要的是能够对该排列进行项目访问,并获得每个项目的索引。也就是说,给定指定排列的
N
i
,一个函数获取索引号并查找驻留在该索引中的数字,另一个函数获取数字并查找其驻留在哪个索引中。我想在
O(1)
中这样做,所以我不需要存储或迭代排列中的每个成员

你说疯了?不可能的?可能是这样。但是请考虑一下:块密码,比如AES,本质上是一个排列,它几乎完成了我上面概述的任务。AES的块大小为16字节,这意味着
N
256^16
,大约是
10^38
。(并非重要的是,
S的大小是惊人的
(256^16)!
,或者大约
10^8507059173023461585843651857942052838
,这打破了我最近的“堆栈溢出上提到的最大数量”记录:

每个AES加密密钥在
N=256^16
上指定一个排列。这种排列不能全部存储在你的计算机上,因为它的成员比太阳系中的原子还要多。但是,它允许您访问项目。通过使用AES加密数据,您可以逐块查看数据,对于每个块(范围(N)
)的成员,您可以输出加密的块,范围(N)
的成员位于排列中原始块的索引号中。当你解密的时候,你在做相反的事情(找到一个块的索引号)。我相信这是在
O(1)
中完成的,我不确定,但无论如何它都非常快

使用AES或任何其他分组密码的问题是,它将您限制在非常特定的
N
,并且它可能只捕获可能的排列的一小部分,而我希望能够使用我喜欢的任何
N
,并对我喜欢的任何排列
S[I]
进行项目访问

在给定大小
N
和排列编号
i
的情况下,是否可以对排列进行
O(1)
项目访问?如果是,怎么做

(如果我有幸在这里得到代码答案,如果它们是Python的,我将不胜感激。)

更新


一些人指出了一个令人悲哀的事实,即排列数字本身将是如此巨大,以至于仅仅读取数字就会使任务变得不可行。然后,我想修改我的问题:如果可以访问排列的字典编号,是否可以在O(尽可能小)中获得排列中的任何项目?

您的问题有点没有意义,因为任意排列索引的输入大小有大小日志(N!)(假设你想表示所有可能的置换),也就是θ(N logn),所以如果N真的很大,那么仅仅读取置换索引的输入将花费太长的时间,肯定比O(1)长得多。存储置换索引的方式可能是,如果你已经存储了它,那么你可以访问O中的元素(1) 但可能任何这样的方法都相当于将置换存储在连续内存中(也有θ(N logn)大小),如果将置换直接存储在内存中,那么假设可以进行O(1)内存访问,问题就变得微不足道了。(但是,您仍然需要考虑元素的位编码的大小,即O(logn))

根据加密类比的精神,也许您应该根据某些属性指定置换的一小部分,并询问是否可以对该小部分进行O(1)或O(logn)元素访问。

编辑:

我误解了这个问题,但它并不是在浪费。我的算法让我明白:置换的字典数字的factoradic表示几乎与置换本身相同。事实上,factoradic表示的第一个数字与对应置换的第一个元素相同(假设您的空间由0到N-1的数字组成)。知道这一点,存储索引而不是排列本身并没有意义。要了解如何将词典数字转换为排列,请阅读下面的内容。 另见

原创帖子:

在S空间中,有N个元素可以填充第一个槽,这意味着有(N-1)!个元素以0开头。因此,i/(N-1)!是第一个元素(我们称之为“a”)。以0开头的S的子集由(N-1)!个元素组成。这些是集合N{a}的可能排列。现在您可以
find_by_index(List N, int i){
    String str = "";
    for(int l = N.length-1; i >= 0; i--){
        int pos = i/fact(l);
        str += N.get(pos);
        N.remove(pos);
        i %= fact(l);
    }
    return str;
}

find_index(String str){
    OrderedList N;
    int i = 0;
    for(int l = str.length-1; l >= 0; l--){
        String item = str.charAt(l);
        int pos = N.add(item);
        i += pos*fact(str.length-l)
    }
    return i;
}
def getPick(fact_num_list):
    """fact_num_list should be a list with the factorial number representation, 
    getPick will return a tuple"""
    result = [] #Desired pick
    #This will hold all the numbers pickable; not actually a set, but a list
    #instead
    inputset = range(len(fact_num_list)) 
    for fnl in fact_num_list:
        result.append(inputset[fnl])
        del inputset[fnl] #Make sure we can't pick the number again
    return tuple(result)
import math

def base10_baseFactorial(number):
    """Converts a base10 number into a factorial base number. Output is a list
    for better handle of units over 36! (after using all 0-9 and A-Z)"""
    loop = 1
    #Make sure n! <= number
    while math.factorial(loop) <= number:
        loop += 1
    result = []
    if not math.factorial(loop) == number:
        loop -= 1 #Prevent dividing over a smaller number than denominator
    while loop > 0:
        denominator = math.factorial(loop)
        number, rem = divmod(number, denominator)
        result.append(rem)
        loop -= 1
    result.append(0) #Don't forget to divide to 0! as well!
    return result