Как вычислить последовательности Bruijn для алфавитов с не-силовым размером?
Я пытаюсь вычислить de Bruijn последовательности для алфавитов, которые имеют несколько символов, которые не являются степенью двух.
Для алфавитов с символами 2 ^ k вычисление последовательностей Bruijn легко: существует несколько простых правил, таких как "Prefernes" и "Prefer Opposites" , которые работают для генерации B (2, n). B (2 ^ k, n) точно совпадает с B (2, kn), если вы читаете 1s и 0s как двоичные коды для фактических символов в вашем алфавите. Например, вы можете интерпретировать B (2,8n) как более n-длинные последовательности байтов.
Предпочитают достаточно просто: напишите n нулей. Затем всегда записывайте одно, если это не приведет к повторению строки длиной n; иначе напишите нуль.
В настоящее время я не вижу, как обобщать такие правила на алфавиты с нестандартными размерами.
Существует общий метод вычисления последовательностей де Брейна через графики: пусть каждая последовательность n-длины, сгенерированная вашим алфавитом, будет node; поместите ребро из A в B, если самые правые n-1 символы A совпадают с крайними левыми n-1 символами B. Пометьте каждое ребро последним символом строки в вершине головы. Любой Эйлеровский путь через этот график будет генерировать последовательность де Брейна, а особая конструкция, которую мы использовали, гарантирует, что будет хотя бы один такой путь. Мы можем использовать Fleury Algorithm для (недетерминистически) построения эйлерова пути:
- Выберите вершину.
- Оставьте эту вершину через некоторое ребро и удалите это ребро, только выбрав ребра, удаление которых приведет к отключению вершины от графика, если альтернативы нет.
- Добавьте в свою строку метку края, который вы только что удалили.
- Перейти к 2, пока все ребра не исчезнут.
Результирующая строка будет последовательностью de Bruijn.
Этот алгоритм несколько сложнее реализовать, чем Prefer Ones. Простота Предпочтений заключается в том, что нужно только проконсультироваться с уже полученным результатом, чтобы определить, что делать. Есть ли простой способ обобщить Prefernes (или, возможно, предпочтительные противоположности) на алфавиты размеров без питания?
Ответы
Ответ 1
Это моя реализация на С++ алгоритма на рисунке 1 из документ Sawada и Ruskey:
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;
Полный текст статьи: Джо Савада и Фрэнк Руске, "Эффективный алгоритм создания ожерелий с фиксированной плотностью", SIAM Journal of Computing 29: 671-684, 1999.
Ответ 2
В соответствии с этой веб-страницей в комбинаторной группе отдела CS в UVic, результат Фридрихсена приводит к тому, что вы можете создать de Bruijn (фактически, лексикографически наименьший), объединяя "лексикографическую последовательность слова Линдона длин, делимых на n". Там даже исходный код для создания последовательности, которую вы можете запросить.
Ответ 3
Вас интересует только обобщение предпочтений или вам нужен не такой сложный алгоритм? Если последнее верно, то может быть полезно помочь рекурсивная реализация Frank Ruskey.
Год назад Я перевел этот файл в 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][email protected][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][email protected][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
Ответ 4
Алгоритм Дюваля делает то же самое итеративно (в 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))
Ответ 5
или вы можете использовать:
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)