排列列表的排序排序

时间:2018-06-16 10:45:29

标签: python permutation lexicographic

我正在尝试开发一种方法,用于在以下列表中查找特定序列的有序排名。

a = list(sorted(itertools.combinations(range(0,5),3)))
b = list(sorted(itertools.permutations(range(0,5),3)))

a表示combinatorial number system的元素列表,因此排名公式非常简单。

我需要的是2个具有以下定义的函数magic_rank_1和magic_rank_2

def magic_rank_1(perm_item,permutation_list_object): 
## permutation_list_object is actually b
    return list_object.index(perm_item)

def magic_rank_2(perm_item,permutation_list_object,combination_list_object):
## permutation_list_object is actually b and combination_list_object is actually a
    return combination_list_object.index(tuple(sorted(perm_item)))

基本上magic_rank_2((0,1,2),b,a) = magic_rank_2((2,0,1),b,a)

听起来很简单..但我有一些限制。

  • 我无法使用indexof函数,因为我无法为每个项目搜索列表> 100000000个项目
  • 我需要magic_rank_1和magic_rank_2纯粹是数学的,不使用任何排序函数或比较函数或搜索函数。我将拥有的所有信息是需要识别其等级的元组和我的字母表的最后一个字母(在这种情况下将是5)
    • 魔法等级2不需要是0到k-1之间的数字,当k = len(a)时,只要它是0到2 ^之间的唯一数字^(上限((max_alphabet / 2)+1))< / LI>

我知道magic_rank_1可以通过与this类似的东西来计算但是有区别,输入字母表中的每个字母都被使用,在我的情况下它是一个子集

最后是的,这应该是散列函数的替代品,目前使用散列函数,但我没有利用magic_rank_2((0,1,2),b,a) = magic_rank_2((2,0,1),b,a)的事实。如果我可以,它将显着减少我的存储空间需求,因为我的序列的长度实际上是5,所以如果我可以计算magic_rank_2的方法,我将我的存储需求减少到当前需求的1%

更新   - 对于magic_rank_2,元组的元素之间不应该进行比较操作,即没有排序,最小值,最大值等

只会使算法的效率低于常规散列

1 个答案:

答案 0 :(得分:0)

以下两个函数将给出一个组合和排列,给定一个单词和一个字母(或者在你的情况下,一个元组和一个列表)。

import itertools
import math

def rank_comb(word, alph, depth=0):
    if not word: return 0

    if depth == 0:
        word = list(word)
        alph = sorted(alph)

    pos = 0
    for (i,c) in enumerate(alph):
        if c == word[0]:
            # Recurse
            new_word = [x for x in word if x != c]
            new_alph = [x for x in alph if x > c]
            return pos + rank_comb(new_word, new_alph, depth+1)
        else:
            num = math.factorial(len(alph)-i-1)
            den = math.factorial(len(alph)-i-len(word)) * math.factorial(len(word)-1)
            pos += num // den


def rank_perm(word, alph, depth=0):
    if not word: return 0

    if depth == 0:
        word = list(word)
        alph = sorted(alph)

    pos = 0
    for c in alph:
        if c == word[0]:
            # Recurse
            new_word = [x for x in word if x != c]
            new_alph = [x for x in alph if x != c]
            return pos + rank_perm(new_word, new_alph, depth+1)
        else:
            num = math.factorial(len(alph)-1)
            den = math.factorial(len(alph)-len(word))
            pos += num // den


#== Validation =====================================================================
# Params
def get_alph(): return range(8)
r = 6

a = list(sorted(itertools.combinations(get_alph(), r)))
b = list(sorted(itertools.permutations(get_alph(), r)))

# Tests
PASS_COMB = True
PASS_PERM = True
for (i,x) in enumerate(a):
    j = rank_comb(x, get_alph())
    if i != j:
        PASS_COMB = False
        print("rank_comb() FAIL:", i, j)

for (i,x) in enumerate(b):
    j = rank_perm(x, get_alph())
    if i != j:
        PASS_PERM = False
        print("rank_perm() FAIL:", i, j)

print("rank_comb():", "PASS" if PASS_COMB else "FAIL")
print("rank_perm():", "PASS" if PASS_PERM else "FAIL")

功能类似,但差异很小:

  • new_alph的过滤方式不同。
  • numden的计算方式不同。

更新

rank_comb2不需要对输入词进行排序(3元组):

import itertools
import math

def rank_comb2(word, alph, depth=0):
    if not word: return 0

    if depth == 0:
        word = list(word)
        alph = sorted(alph)

    pos = 0
    for (i,c) in enumerate(alph):
        if c == min(word):
            # Recurse
            new_word = [x for x in word if x != c]
            new_alph = [x for x in alph if x > c]
            return pos + rank_comb2(new_word, new_alph, depth+1)
        else:
            num = math.factorial(len(alph)-i-1)
            den = math.factorial(len(alph)-i-len(word)) * math.factorial(len(word)-1)
            pos += num // den

r1 = rank_comb2([2,4,1], range(5))
r2 = rank_comb2([1,4,2], range(5))
r3 = rank_comb2([4,1,2], range(5))

print(r1, r2, r3)     # 7 7 7