代码之家  ›  专栏  ›  技术社区  ›  Siddharth Chabra

从C中的n生成k置换

  •  2
  • Siddharth Chabra  · 技术社区  · 7 年前

    我基本上需要以下Python的等效结果 itertools C中的命令:

    a = itertools.permutations(range(4),2))

    目前,我的过程包括首先从10中“选择”5个元素,然后为这5个元素生成排列,如图所示。 here

    这种方法的问题在于输出的顺序。我需要它是(a),而我得到的是(b),如下所示。

    a = itertools.permutations(range(4),2)
    for i in a:
        print(i)
    
    (0, 1)
    (0, 2)
    (0, 3)
    (1, 0)
    (1, 2)
    (1, 3)
    (2, 0)
    (2, 1)
    (2, 3)
    (3, 0)
    (3, 1)
    (3, 2)
    
    b = itertools.combinations(range(4),2) 
    for i in b:
        c = itertools.permutations(i)
        for j in c:
            print(j)
    (0, 1)
    (1, 0)
    (0, 2)
    (2, 0)
    (0, 3)
    (3, 0)
    (1, 2)
    (2, 1)
    (1, 3)
    (3, 1)
    (2, 3)
    (3, 2)
    

    我使用的另一种方法如下

    void perm(int n, int k)
    {
        bool valid = true;
        int h = 0, i = 0, j = 0, limit = 1;
        int id = 0;
        int perm[10] = { 0,0,0,0,0,0,0,0,0,0 };
        for (i = 0; i < k; i++)
            limit *= n;
        for (i = 0; i < limit; i++)
        {
            id = i;
            valid = true;
            for (j = 0; j < k; j++)
            {
                perms[j] = id % n;
                id /= n;
                for (h = j - 1; h >= 0; h--)
                    if (perms[j] == perms[h])
                    {
                        valid = false; break;
                    }
                if (!valid) break;
            }
            if (valid)
            {
                for (h = k - 1; h > 0; h--)
                    printf("%d,", perms[h]);
                printf("%d\n", perms[h]);
                count++;
            }
        }
    }
    

    记忆是我的约束,所以我不能无限期地存储排列。性能需要比上面的算法更好,比如 n 是50和 k 是10,我最终迭代了更多无效的组合(60+%)

    我知道 heaps algorithm 为了在适当的位置生成排列,它再次为整个数组生成,而不是像我需要的那样生成n的k。

    问题。

    1. 有没有比迭代n^k次更好的方法?
    2. 我可以做一个懒惰的迭代器,在给定当前排列的情况下移动到下一个排列吗?

    编辑 这不是STD::NExtx置换实现的复制,因为它将置换和输入的整个范围。 我已经清楚地提到了,我需要k置换n。如果我的范围是10,我希望长度(k)的所有排列都是5,STD::NExtx置换在长度或排列与输入范围的长度相同时起作用。

    更新 这里有一个丑陋的递归nextperm解决方案,它比我以前的解决方案快4倍,并且提供了类似于python懒惰迭代器的增量nextperm。

    int nextPerm(int perm[], int k, int n)
    {
        bool invalid = true;
        int subject,i;
        if (k == 1)
        {
            if (perm[0] == n - 1)
                return 0;
            else { perm[0]=perm[0]+1; return 1; }
        }
        subject = perm[k - 1]+1;
    
        while (invalid)
        {
            if (subject == n)
            {
                subject = 0;
                if (!nextPerm(perm, k - 1, n))
                    return 0;
            }
            for (i = 0; i < k-1; i++)
            {
                if (perm[i] != subject)
                    invalid = false;
                else
                {
                    invalid = true;subject++; break; 
                }
            }
        }
        perm[k - 1] = subject;
        return 1;
    }
    int main()
    {
        int a, k =3 ,n = 10;
        int perm2[3] = { 0,1,2}; //starting permutation
        unsigned long long count = 0;
        int depth = 0;
        do
        {
            for (a = 0; a < k - 1; a++)
                printf("%d,", perm2[a]);
            printf("%d\n", perm2[k - 1]);
            count++;
        }
        while (nextPerm(perm2,k,n));
        printf("\n%llu", count);
        getchar();
        return 0;
    }
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   rici    7 年前

    std::next_permutation

    n-k

    std::reverse

    next-permutation next_k_permutation k

    std::reverse(); std::next_permutation();

    #include <stddef.h>
    
    /* Helper functions */
    static void swap(int* elements, size_t a, size_t b) {
      int tmp = elements[a]; elements[a] = elements[b]; elements[b] = tmp;
    }
    static void flip(int* elements, size_t lo, size_t hi) {
      for (; lo + 1 < hi; ++lo, --hi) swap(elements, lo, hi - 1);
    }
    
    /* Given an array of n elements, finds the next permutation in
     * lexicographical order with a different k-prefix; in effect, it 
     * generates all k-permutations of the array.
     * It is required that the suffix be sorted in ascending order. This
     * invariant will be maintained by the function.
     * Before the first call, the array must be sorted in ascending order.
     * Returns true unless the input is the last k-permutation.
     */ 
    int next_k_permutation(int* elements, size_t n, size_t k) {
      // Find the rightmost element which is strictly less than some element to its
      // right.
      int tailmax = elements[n - 1];
      size_t tail = k;
      while (tail && elements[tail - 1] >= tailmax)
        tailmax = elements[--tail];
      // If no pivot was found, the given permutation is the last one.
      if (tail) {
        size_t swap_in;
        int pivot = elements[tail - 1];
        // Find the smallest element strictly greater than the pivot, either
        // by searching forward from the pivot or backwards from the end.
        if (pivot >= elements[n - 1]) {
          for (swap_in = tail; swap_in + 1 < k && elements[swap_in + 1] > pivot; ++swap_in) {}
        } else {
          for (swap_in = n - 1; swap_in > k && elements[swap_in - 1] > pivot; --swap_in) {}
        }
        // Swap the pivots
        elements[tail - 1] = elements[swap_in];
        elements[swap_in] = pivot;
        // Flip the tail. 
        flip(elements, k, n);
        flip(elements, tail, n);
      }
      return tail;
    }
    

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int intcmp(const void* a, const void* b) {
      return *(int*)a < *(int*)b ? -1 : 
             *(int*)a > *(int*)b ?  1 :
                                    0 ;
    }
    
    int main(int argc, char** argv) {
      size_t k = (argc > 1) ? atoi(argv[1]) : 0;
      if (argc < k + 2) {
        fprintf(stderr, "Usage: %s K element...\n"
                        "       where K <= number of elements\n",
                        argv[0]);
        return 1;
      }
      size_t n = argc - 2;
      int elements[n];
      for (int i = 0; i < n; ++i) elements[i] = atoi(argv[i + 2]);
      qsort(elements, n, sizeof *elements, intcmp);
      do {
        const char* delimiter = "";
        for (size_t i = 0; i < k; ++i) {
          printf("%s%2d ", delimiter, elements[i]);
          delimiter = " ";
        }
        putchar('\n');
      } while (next_k_permutation(elements, n, k));
      return 0;
    }
    

    $ ./k_next_permutation 2 7 3 4 4 5
     3   4 
     3   5 
     3   7 
     4   3 
     4   4 
     4   5 
     4   7 
     5   3 
     5   4 
     5   7 
     7   3 
     7   4 
     7   5 
    

    n - k

    #include <assert.h>
    #include <stdbool.h>
    #include <stddef.h>
    
    /* Helper functions */
    static void swap(int* elements, size_t a, size_t b) {
      int tmp = elements[a]; elements[a] = elements[b]; elements[b] = tmp;
    }
    static void flip(int* elements, size_t lo, size_t hi) {
      for (; lo + 1 < hi; ++lo, --hi) swap(elements, lo, hi - 1);
    }
    static void rotate_left(int* elements, size_t lo, size_t hi) {
      if (hi > lo) {
        int tmp = elements[lo];
        for (size_t i = lo + 1; i < hi; ++i) elements[i - 1] = elements[i];
        elements[hi - 1] = tmp;
      }
    }
    
    /* Recursive function; the main function will fill in the extra parameters */
    /* Requires hi >= lo and hi >= k. Array must have size (at least) lo + k */    
    static bool helper(int* array, size_t lo, size_t k, size_t hi,
                           bool(*process)(void*, int*, size_t), void* baton) {
      if (hi == lo) {
        if (!process(baton, array + lo, k)) return false;
        if (lo % 2)
          flip(array, 0, lo);
        else
          rotate_left(array, 0, lo);
      }
      else {
        for (size_t i = 0; i < hi - 1; ++i) {
          if (!helper(array, lo, k, hi - 1, process, baton))
            return false;
          swap(array, hi % 2 ? 0 : i, hi - 1);
        }
        if (!helper(array, lo, k, hi - 1, process, baton))
          return false;
      }
      return true;
    }
    
    /* Generate all k-permutations of the given array of size n.
     * The process function is called with each permutation; if it returns false,
     * generation of permutations is terminated.
     */ 
    bool k_heap_permute(int* array, size_t n, size_t k,
                        bool(*process)(void*, int*, size_t), void* baton) {
      assert(k <= n);
      return helper(array, n - k, k, n, process, baton);
    }
    

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    bool print_array(void* vf, int* elements, size_t n) {
      FILE* f = vf;
      const char* delim = "";
      for (size_t i = 0; i < n; ++i) {
        fprintf(f, "%s%2d", delim, elements[i]);
        delim = " ";
      }
      putc('\n', f);
      return true;
    }
    
    int main(int argc, char** argv) {
      size_t k = (argc > 1) ? atoi(argv[1]) : 0;
      if (argc < k + 2) {
        fprintf(stderr, "Usage: %s K element...\n"
                        "       where K <= number of elements\n",
                        argv[0]);
        return 1;
      }
      size_t n = argc - 2;
      int elements[n];
      for (int i = 0; i < n; ++i)
        elements[i] = atoi(argv[i + 2]);
      k_heap_permute(elements, n, k, print_array, stdout);
      return 0;
    }
    

    $ ./permut 2      1 5 9 7 3
     7  3
     9  3
     5  3
     1  3
     1  5
     7  5
     9  5
     3  5
     3  9
     1  9
     7  9
     5  9
     5  7
     3  7
     1  7
     9  7
     9  1
     5  1
     3  1
     7  1
    
    推荐文章