在Scheme中生成项链的简单算法很好?

时间:2008-11-04 18:23:40

标签: scheme combinatorics necklaces

长度为n的k-ary项链是长度为n的有序列表,其项目是从长度为k的字母表中提取的,这是按字典顺序排列的第一个列表,在所有列表中共享旋转下的排序。

实施例: (1 2 3)和(1 3 2)是字母{1 2 3}中长度为3的项链。

更多信息: http://en.wikipedia.org/wiki/Necklace_(combinatorics)

我想在Scheme(或您选择的Lisp)中生成这些。我找到了一些文件...... Savage - 一种新的项链生成算法 泽田 - 恒定摊销时间生成手镯
泽田 - 用禁止的子串生成项链
......但是它们中的代码对我来说是不透明的。主要是因为他们似乎没有传递字母或所需的长度(n)。我正在寻找的方案程序是形式的(项链n'(a b c ...))。

通过首先生成k ^ n个列表然后过滤掉旋转,我可以很容易地生成这些。但它的内存效率非常低......

谢谢!

4 个答案:

答案 0 :(得分:3)

用于生成项链的FKM算法。 PLT计划。性能不是那么热。它会将任何内容作为字母表,并将内部数字映射到您提供的任何内容上。似乎是正确的;没有保证。在翻译循环时我很懒,所以你得到for循环和逃避延续的奇怪组合。

(require srfi/43)

(define (gennecklaces n alphabet)
  (let* ([necklaces '()]
         [alphavec (list->vector alphabet)]
         [convert-necklace
          (lambda (vec)
            (map (lambda (x) (vector-ref alphavec x)) (cdr (vector->list vec))))]
         [helper
          (lambda (n k)
            (let ([a (make-vector (+ n 1) 0)]
                  [i n])
              (set! necklaces (cons (convert-necklace a) necklaces))
              (let/ec done
                (for ([X (in-naturals)])
                  (vector-set! a i (add1 (vector-ref a i)))
                  (for ([j (in-range 1 (add1 (- n i)))])
                    (vector-set! a (+ j i)
                                 (vector-ref a j)))
                  (when (= 0 (modulo n i))
                    (set! necklaces (cons (convert-necklace a) necklaces)))
                  (set! i n)
                  (let/ec done
                    (for ([X (in-naturals)])
                      (unless (= (vector-ref a i)
                                 (- k 1))
                        (done))
                      (set! i (- i 1))))
                  (when (= i 0)
                    (done))))))])
    (helper n (length alphabet))
    necklaces))

答案 1 :(得分:0)

我会做两步。首先,从字母表中找到n个元素的每个组合。然后,对于每个组合,选择最低值,并生成剩余项目的所有排列。

编辑:这是一些代码。它假定输入列表已经排序并且不包含重复项。

(define (choose n l)
  (let ((len (length l)))
    (cond ((= n 0) '(()))
          ((> n len) '())
          ((= n len) (list l))
          (else (append (map (lambda (x) (cons (car l) x))
                             (choose (- n 1) (cdr l)))
                        (choose n (cdr l)))))))

(define (filter pred l)
  (cond ((null? l) '())
        ((pred (car l)) (cons (car l) (filter pred (cdr l))))
        (else (filter pred (cdr l)))))

(define (permute l)
  (cond ((null? l) '(()))
        (else (apply append 
                     (map (lambda (x)
                             (let ((rest (filter (lambda (y) (not (= x y))) l)))
                               (map (lambda (subperm) (cons x subperm))
                                    (permute rest))))
                      l)))))

(define (necklaces n l)
  (apply
   append
   (map
    (lambda (combination)
      (map (lambda (permutation)
              (cons (car combination) permutation))
           (permute (cdr combination))))
    (choose n l))))


(display (choose 1 '(1 2 3 4 5))) (newline)
(display (choose 2 '(1 2 3 4 5))) (newline)
(display (permute '(1 2))) (newline)
(display (permute '(1 2 3))) (newline)
(display (necklaces 3 '(1 2 3 4))) (newline)
(display (necklaces 2 '(1 2 3 4))) (newline)

答案 2 :(得分:0)

  

示例:(1 2 3)和(1 3 2)是字母{1 2 3}中长度为3的项链。

你忘了(1 1 1)(1 1 2)(1 1 3)(1 2 2)(1 3 3)(2 2 2)(2 2 3)(2 3 3)(3 3 3) 。项链可以包含重复项。

如果你只是在寻找长度为N的项链,从大小为N的字母表中抽出,其中包含 no 重复项,那么它很简单:会有(N-1)!项链,每条项链的形式为(1 :: perm),其中perm是{2 .. N}的任何排列。例如,{1 .. 4}的项链将是(1 2 3 4)(1 2 4 3)(1 3 2 4)(1 3 4 2)(1 4 2 3)(1 4 3 2) 。扩展该方法以处理长度为K <1的无重复项链。 N留给读者练习。

但如果你想找到可能包含重复元素的真项链,那就不那么简单了。

答案 3 :(得分:0)

作为第一个想法,你可以做到明显但效率低下:逐步完成所有组合并检查它们是否是项链,即它们是否是元素的词法最小旋转(上文中第5页的正式定义) 。这就像你提出的那样,但是你会在生成所有非项链后立即扔掉它们。

除此之外,我认为你必须理解这篇文章(http://citeseer.ist.psu.edu/old/wang90new.html):

吨。 Wang和C. Savage,“一种新的项链生成算法”,报告       TR-90-20,北卡罗来纳州立大学计算机科学系       (1990)。

这不是太难,您可以通过实施所述的tausigma函数将其细分,然后按照文章中列出的顺序应用它们。