我正在尝试为字母计算de Bruijn sequences,这些字母的字符数不是2的幂。
对于具有2 ^ k个字符的字母表,计算de Bruijn序列很简单:有几个简单的规则,例如"Prefer Ones" and "Prefer Opposites"用于生成B(2,n)。如果您将1和0作为字母表中实际字符的二进制代码读取,则B(2 ^ k,n)与B(2,kn)完全相同。例如,您可以将B(2,8n)解释为超过n长度的字节序列。
首选Ones很简单:写n个零。然后,总是写一个,除非它会导致重复n长度的字符串;否则,写一个零。
目前,我没有看到如何将这些规则推广到两个不同功率的字母表。
有一种通过图表计算de Bruijn序列的一般方法:让你的字母表生成的每个n长度序列都是一个节点;如果A的最右边的n-1个字符与B的最左边的n-1个字符相同,则将A到B的边放置。用头顶点中的字符串的最后一个字符标记每个边。通过该图的任何Eulerian path将生成de Bruijn序列,并且我们使用的特殊构造保证将存在至少一个这样的路径。我们可以使用Fleury's Algorithm(非确定性地)构建欧拉路径:
结果字符串将是de Bruijn序列。
这种算法比Prefer Ones实现起来要复杂一些。 Prefer Ones的简单之处在于,只需查阅已生成的输出即可确定要执行的操作。有没有一种简单的方法可以将Prefer Ones(或者,可能更喜欢Opposites)推广到非幂次大小的字母表?
答案 0 :(得分:10)
这是我在{1}}中的算法的C ++实现:
void debruijn(unsigned int t,
unsigned int p,
const unsigned int k,
const unsigned int n,
unsigned int* a,
boost::function<void (unsigned int*,unsigned int*)> callback)
{
if (t > n) {
// we want only necklaces, not pre-necklaces or Lyndon words
if (n % p == 0) {
callback(a+1, a+p+1);
}
}
else {
a[t] = a[t-p];
debruijn(t+1, p, k, n, a, callback);
for (unsigned int j = a[t-p]+1; j < k; ++j) {
a[t] = j;
debruijn(t+1, t, k, n, a, callback);
}
}
}
struct seq_printer {
const std::vector<char>& _alpha;
seq_printer(const std::vector<char>& alpha) : _alpha(alpha) {}
void operator() (unsigned int* a, unsigned int* a_end) const {
for (unsigned int* i = a; i < a_end; ++i) {
std::cout << _alpha[*i];
}
}
};
...
std::vector<char> alpha;
alpha.push_back('a');
alpha.push_back('b');
alpha.push_back('c');
unsigned int* a = new unsigned int[N+1];
a[0] = 0;
debruijn(1, 1, alpha.size(), N, a, seq_printer(alpha));
if (N > 1) std::cout << alpha[0];
std::cout << std::endl;
delete[] a;
该论文的完整参考资料是:Joe Sawada和Frank Ruskey,“一种生成固定密度项链的高效算法”,SIAM计算杂志 29 :671-684,1999。
答案 1 :(得分:4)
根据UVic CS部门组合小组的this web page,有一个结果是由于Fredericksen你可以通过连接“词典序列”来生成de Bruijn序列(事实上,词典中最小的序列) {n>整数的Lyndon words长度。甚至还有源代码来构建您可以请求的序列。
答案 2 :(得分:3)
您是否只对Prefer Ones的概括感兴趣,还是只想要一个不那么复杂的算法?如果后者是真的,那么Frank Ruskey的递归实现可能会有所帮助。
一年前I translated that one给Ruby。
# De Bruijn sequence
# Original implementation by Frank Ruskey (1994)
# translated to C by Joe Sawada
# and further translated to Ruby by Jonas Elfström (2009)
@n=4
@k=10
@a=[0]
@sequence=[]
def debruijn(t, p, alike)
if t>@n
if @n%p==0
1.upto(p) {|j| @sequence<<@a[j]}
end
else
@a[t]=@a[t-p]
if @a[t]>0
debruijn(t+1,p,alike+1)
else
debruijn(t+1,p,alike)
end
(@a[t-p]+1).upto(@k-1) {|j|
@a[t]=j
debruijn(t+1,t,alike+1)
}
end
end
debruijn(1,1,0)
print @sequence.join
Uckelman注意到alike
变量什么也没做。以下内容产生相同的序列。
@n=4
@k=10
@a=[0]
@sequence=[]
def debruijn(t, p)
if t>@n
if @n%p==0
1.upto(p) {|j| @sequence<<@a[j]}
end
else
@a[t]=@a[t-p]
debruijn(t+1,p)
(@a[t-p]+1).upto(@k-1) {|j|
@a[t]=j
debruijn(t+1,t)
}
end
end
debruijn(1,1)
print @sequence.join
答案 3 :(得分:0)
Duval的算法迭代地做同样的事情(这次在Python中):
def debruijn(k, n):
v = [0 for _ in range(n)]
l = 1
r = []
while True:
if n % l == 0:
r.extend(v[0:l])
for i in range(l, n):
v[i] = v[i-l]
l = n
while l > 0 and v[l-1] >= k-1:
l-=1
if l == 0:
break
v[l-1] += 1
return r
print(debruijn(3,5))
答案 4 :(得分:0)
或者您可以使用:
def de_bruijn(k, n):
a = [0] * k * n
sequence = []
def db(t, p):
if t > n:
if n % p == 0:
for j in range(1, p + 1):
sequence.append(a[j])
else:
a[t] = a[t - p]
db(t + 1, p)
for j in range(a[t - p] + 1, k):
a[t] = j
db(t + 1, t)
db(1, 1)
return sequence
print de_bruijn(2,9)
答案 5 :(得分:0)
基于@ stefan-gruenwald的代码,该代码缺少a simply categorizable set of words。尽管我还不能修改它,但我还是写了几行来查找错误,这似乎是
import itertools
def debruijn (k, n) :
v = [ 0 for _ in range (n) ]
l = 1
r = []
while True :
if n % l == 0 :
r.extend (v [0:l])
for i in range (l, n) :
v [i] = v [i-l]
l = n
while l > 0 and v [l-1] >= k-1 :
l -= 1
if l == 0 :
break
v [l-1] += 1
return r
K = int (input ( 'k ' ) )
N = int (input ( 'n ' ) )
for k in range (K) : # length of alphabet
for n in range (N) : # length of word(s)
List = debruijn (k, n)
l = ''
for L in List :
l += str (L)
L = itertools.product (range (k), repeat = n)
print ( 'alphabet k', k, '\tword n', n, str ('|' + l + '|') )
for a in L :
searchstr = ''
for A in a :
searchstr += str (A)
if not searchstr in l :
print ( searchstr, end = ' ' )
print ()
还可以保存在文件中:
import itertools
def debruijn (k, n) :
v = [ 0 for _ in range (n) ]
l = 1
r = []
while True :
if n % l == 0 :
r.extend (v [0:l])
for i in range (l, n) :
v [i] = v [i-l]
l = n
while l > 0 and v [l-1] >= k-1 :
l -= 1
if l == 0 :
break
v [l-1] += 1
return r
K = int (input ( 'k ' ) )
N = int (input ( 'n ' ) )
for k in range (K) : # length of alphabet
for n in range (N) : # length of word(s)
List = debruijn (k, n)
l = ''
for L in List :
l += str (L)
L = itertools.product (range (k), repeat = n)
with open (str (k) + '-' + str (n), 'w') as f :
f.write ( 'alphabet length ' + str (k) + '\tword length ' + str (n) + '\n' + l + '\nnot in:\n' )
for a in L :
searchstr = ''
for A in a :
searchstr += str (A)
if not searchstr in l :
f.write ( searchstr + ' ' )