【问题标题】:Ordered ranking for a list of permutations排列列表的有序排名
【发布时间】:2018-06-16 10:45:28
【问题描述】:

我正在尝试开发一种方法来查找以下列表中特定序列的有序等级。

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)
    • 当 k = len(a) 时,magic rank 2 不必是 0 到 k-1 之间的数字,只要它是 0 到 2 之间的唯一数字即可^(ceiling((max_alphabet/2)+1))

我知道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,元组的元素之间应该没有比较操作,即没有排序、最小值、最大值等

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

【问题讨论】:

  • indexof 函数最好在散列中实现,就像你说的那样,如果你能负担得起预先存储和索引列表以提高性能。否则,由于itertools.combinationsitertools.permutations 都是生成器,这意味着每次调用它们时都必须花时间迭代整个列表,而且速度很慢。您在 magic_rank_2 中的输入只需排序一次。如果需要多次调用magic_rank_2,可以将循环包裹在Cython函数中。
  • 至于数学(?)方法,可以看itertools的源码,了解生成器迭代中元素的预期顺序。然后您可以尝试根据预期顺序计算 perm_item 的确切索引。我不会那样做,因为没有人知道这是否不是一个 NP 难题并且需要博士学位。论文。
  • 我不会称其为 NP 难题,最终的实现是针对 c 的,无论如何我使用排序树进行排序(即 log(n))但因为我的话是 7-10数字 longs 这意味着用于排序的 13-22 比较操作被证明是一个瓶颈,我还需要利用受控冲突,因为 rank(x,y,z) = rank(y,z,x)
  • @Mai 1)Python 用于概念验证和原型设计,因此 python 中的有效算法适用于 C 2) 我没有说树排序我说排序树有很大的不同 3)我在这里要求一种更有效的做事方式。与其为你的答案拼命投票,不如不回答如果你忍不住希望没有下次
  • 很抱歉我不能。祝你好运。

标签: python permutation lexicographic


【解决方案1】:

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

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 不需要对输入词进行排序(一个三元组):

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

【讨论】:

  • 这只处理magic_rank_1,所以不能将其标记为答案,否则效果很好
  • 没问题,但您能否扩展magic_rank_2 的功能?或者rank_comb(sorted(word), alph) 没有做你想做的事情?
  • 我已经提到了..我不想对它显着降低性能的词进行排序,寻找一个纯粹的数学函数
  • 对 3 元素元组进行排序会显着降低性能?
  • 无论如何,我用一种不需要对输入单词进行排序的方法更新了答案,尽管它似乎不太效率,而不是更多。
猜你喜欢
  • 2017-05-07
  • 2011-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-27
  • 2018-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多