【问题标题】:Get a permutation as a function of a unique given index in O(n)得到一个排列作为 O(n) 中唯一给定索引的函数
【发布时间】:2014-08-03 14:52:46
【问题描述】:

我想要一个函数get_permutation,给定一个列表l和一个索引i,返回一个l的排列,这样排列对于所有大于i的@987654327都是唯一的@ 且低于 n!(其中 n = len(l))。

get_permutation(l,i) != get_permutation(l,j) 如果i!=j 对于所有ij s.t. 0 <= i and j < len(l)!)。

此外,此函数必须在O(n) 中运行。

例如,如果不是指数阶,这个函数将符合要求:

def get_permutation(l, i):
     return list(itertools.permutations(l))[i]

有没有人能解决上述问题?

编辑:我想要排列来自索引而不是来自排列的索引

【问题讨论】:

  • 您是否需要特定的排列顺序,或者您只是希望每个 i 返回一个唯一且一致的排列?
  • 只是对于每个i,您都会得到一个独特且一致的排列:我并不关心顺序
  • 它怎么可能在i0n! 之间运行?
  • @barakmanos 这就是重点,OP 不想生成所有排列并为其编制索引,而是确定l 的哪个排列将位于索引i 处。

标签: python algorithm permutation


【解决方案1】:

如果您不关心哪些排列得到哪些索引,则 O(n) 解决方案成为可能如果我们认为任意整数的算术运算是 O(1)

例如,请参阅 Wendy Myrvold 和 Frank Ruskey 的论文“Ranking and unranking permutations in linear time”。

简而言之,有两个想法。


(1) 考虑Fisher-Yates shuffle 方法生成随机排列(伪代码如下):

p = [0, 1, ..., n-1]
for i := 0 upto n-1:
    j := random_integer (0, i)
    exchange p[i] and p[j]

这种变换是单射的:如果我们给它一个不同的随机整数序列,它肯定会产生不同的排列。因此,我们将随机整数替换为非随机整数:第一个是 0,第二个是 0 或 1,...,最后一个可以是 0 到 n-1 之间的任何整数。


(2) 有 n! n阶排列。我们现在要做的是在factorial number system中写一个从0到n!-1的整数:最后一位永远是0,前一位是0或者1,……,从0到n有n种可能n-1 表示第一个数字。因此,我们将获得一个唯一的序列来提供上述伪代码。

现在,如果我们考虑将我们的数字除以从 1 到 n 的整数是 O(1) 操作,那么将数字转换为阶乘系统是 O(n) 这样的除法。严格来说,这是不正确的:对于大 n,数字 n!包含 O(n log n) 二进制数字的顺序,并且该除法的成本与位数成正比。


在实践中,对于小的 n,O(n^2) 或 O(n log n) 方法对排列进行排序或取消排序,以及需要 O(2^n) 或 O(n!) 内存来存储的方法一些预先计算的值可能比涉及整数除法的 O(n) 方法更快,这在现代处理器上是一种相对较慢的操作。 对于 n 足够大,使得 n!不适合机器字,“O(n) if order-n! integer operations are O(1)”参数停止工作。所以,如果你不坚持理论上 O(n),你可能对小 n 和大 n 都更好。

【讨论】:

  • 如何使用 O(n) 算术运算计算阶乘数系统中数字的表示?正如我所看到的:找到第一个数字需要 O(n) 次操作,然后您必须递归处理其余数字 - 因此需要 O(n^2) 次操作(在最坏和平均情况下)。
  • @coproc:从另一端开始!通过取数字模 1 计算最后一位,然后除以 1。通过取当前数字模 2 计算倒数第二位,然后除以 2。依此类推。
【解决方案2】:

基于http://www.2ality.com/2013/03/permutations.html,这是一个可能的解决方案。正如@Gassa 指出的那样, elements.pop 的顺序不是恒定的,因此解决方案在列表的长度上不是线性的。因此,我不会将此标记为已接受的答案。但是,它完成了工作。

def integerToCode(idx, permSize):                                                                                                                                       
    if (permSize <= 1):                                                                                                                                                 
        return [0]                                                                                                                                                      
    multiplier = math.factorial(permSize-1)                                                                                                                             
    digit =idx / multiplier                                                                                                                                             
    return [digit] +  integerToCode(idx % multiplier, permSize-1)                                                                                                       


def codeToPermutation(elements, code):                                                                                                                                  
    return map(lambda i: elements.pop(i), code)                                                                                                                         

def get_permutation(l, i):                                                                                                                                              
    c = integerToCode(i, len(l))                                                                                                                                        
    return codeToPermutation(list(l), c)

【讨论】:

    【解决方案3】:

    更新:Finding n-th permutation without computing others 可能被骗,算法见那里。

    如果len(l) 很小,您可以预先计算perm_index = permutations(range(len(l))) 并将其用作实际数据的索引列表。

    此外,如果您有来自 range(len(l)) 的排列列表,并且您需要一个用于 range(len(l) - 1) 的排列,您可以执行以下操作:

    [x - 1 for x in perm_index[i][1:]]
    

    这利用了排列在生成时按排序顺序排列的事实。

    【讨论】:

    • 但这不是一个合理的解决方案,因为顺序:如果我想访问列表中的最后一个排列,我必须先预先计算所有其他排列。
    【解决方案4】:

    此解决方案适用于 O(1)(运行时复杂性;字典查找的摊销成本):

    代码

    #!/usr/bin/env python
    
    import itertools
    
    
    def get_permutation():
        memoize = {}
    
        def _memoizer(l, i):
            if str(l) in memoize and i not in memoize[str(l)]:
                memoize[str(l)][i] = memoize[str(l)]['permutations'].next()
            else:
                p = itertools.permutations(l)
                memoize[str(l)] = {'permutations': p}
                memoize[str(l)][i] = memoize[str(l)]['permutations'].next()
            return memoize[str(l)][i]
        return _memoizer
    
    if __name__ == '__main__':
        get_permutation = get_permutation()
        l1 = list(range(10))
        l2 = list(range(5))
        print(get_permutation(l1, 1))
        print(get_permutation(l1, 20))
        print(get_permutation(l2, 3))
    

    输出

    (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    (0, 1, 2, 3, 4, 5, 6, 7, 9, 8)
    (0, 1, 2, 3, 4)
    

    工作原理

    代码将所有过去的调用存储在字典中。它还存储排列对象。因此,如果请求新的排列,则使用下一个排列。

    代码使用itertools.permutations

    【讨论】:

    • 好的,这就是我所要求的。但是,我希望解决方案具有参考透明度。这个解决方案没有。
    • 很好地解决了这里的问题!如果我们将“O(1) 中的下一个排列”策略更改为“O(n) 中的新随机排列”,这对于某些应用程序来说可能是一种可行的方法。
    【解决方案5】:

    有点太晚了... C# 代码应该会给你你期望的结果:

    using System;
    using System.Collections.Generic;
    
    namespace WpfPermutations
    {
        public class PermutationOuelletLexico3<T>
        {
            // ************************************************************************
            private T[] _sortedValues;
    
            private bool[] _valueUsed;
    
            public readonly long MaxIndex; // long to support 20! or less 
    
            // ************************************************************************
            public PermutationOuelletLexico3(T[] sortedValues)
            {
                if (sortedValues.Length <= 0)
                {
                    throw new ArgumentException("sortedValues.Lenght should be greater than 0");
                }
    
                _sortedValues = sortedValues;
                Result = new T[_sortedValues.Length];
                _valueUsed = new bool[_sortedValues.Length];
    
                MaxIndex = Factorial.GetFactorial(_sortedValues.Length);
            }
    
            // ************************************************************************
            public T[] Result { get; private set; }
    
            // ************************************************************************
            /// <summary>
            /// Return the permutation relative to the index received, according to 
            /// _sortedValues.
            /// Sort Index is 0 based and should be less than MaxIndex. Otherwise you get an exception.
            /// </summary>
            /// <param name="sortIndex"></param>
            /// <param name="result">Value is not used as inpu, only as output. Re-use buffer in order to save memory</param>
            /// <returns></returns>
            public void GetValuesForIndex(long sortIndex)
            {
                int size = _sortedValues.Length;
    
                if (sortIndex < 0)
                {
                    throw new ArgumentException("sortIndex should be greater or equal to 0.");
                }
    
                if (sortIndex >= MaxIndex)
                {
                    throw new ArgumentException("sortIndex should be less than factorial(the lenght of items)");
                }
    
                for (int n = 0; n < _valueUsed.Length; n++)
                {
                    _valueUsed[n] = false;
                }
    
                long factorielLower = MaxIndex;
    
                for (int index = 0; index < size; index++)
                {
                    long factorielBigger = factorielLower;
                    factorielLower = Factorial.GetFactorial(size - index - 1);  //  factorielBigger / inverseIndex;
    
                    int resultItemIndex = (int)(sortIndex % factorielBigger / factorielLower);
    
                    int correctedResultItemIndex = 0;
                    for(;;)
                    {
                        if (! _valueUsed[correctedResultItemIndex])
                        {
                            resultItemIndex--;
                            if (resultItemIndex < 0)
                            {
                                break;
                            }
                        }
                        correctedResultItemIndex++;
                    }
    
                    Result[index] = _sortedValues[correctedResultItemIndex];
                    _valueUsed[correctedResultItemIndex] = true;
                }
            }
    
            // ************************************************************************
            /// <summary>
            /// Calc the index, relative to _sortedValues, of the permutation received
            /// as argument. Returned index is 0 based.
            /// </summary>
            /// <param name="values"></param>
            /// <returns></returns>
            public long GetIndexOfValues(T[] values)
            {
                int size = _sortedValues.Length;
                long valuesIndex = 0;
    
                List<T> valuesLeft = new List<T>(_sortedValues);
    
                for (int index = 0; index < size; index++)
                {
                    long indexFactorial = Factorial.GetFactorial(size - 1 - index);
    
                    T value = values[index];
                    int indexCorrected = valuesLeft.IndexOf(value);
                    valuesIndex = valuesIndex + (indexCorrected * indexFactorial);
                    valuesLeft.Remove(value);
                }
                return valuesIndex;
            }
    
            // ************************************************************************
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-09-05
      • 2022-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多