Как я могу генерировать последовательности де Брейна итеративно? - PullRequest
0 голосов
/ 03 июля 2019

Я ищу способ генерировать последовательность де Брюина итеративно, а не с помощью рекурсии.Моя цель - генерировать его символ за символом.

Я нашел пример кода на Python для генерации последовательностей де Брюина и перевел его на Rust.Я еще не в состоянии понять эту технику достаточно хорошо, чтобы создать свой собственный метод.

Переведено в Rust:

fn gen(sequence: &mut Vec<usize>, a: &mut [usize], t: usize, p: usize, k: usize, n: usize) {
    if t > n {
        if n % p == 0 {
            for x in 1..(p + 1) {
                sequence.push(a[x])
            }
        }
    } else {
        a[t] = a[t - p];
        gen(sequence, a, t + 1, p, k, n);
        for x in (a[t - p] + 1)..k {
            a[t] = x;
            gen(sequence, a, t + 1, t, k, n);
        }
    }
}

fn de_bruijn<T: Clone>(alphabet: &[T], n: usize) -> Vec<T> {
    let k = alphabet.len();
    let mut a = vec![0; n + 1];
    let vecsize = k.checked_pow(n as u32).unwrap();
    let mut sequence = Vec::with_capacity(vecsize);
    gen(&mut sequence, &mut a, 1, 1, k, n);
    sequence.into_iter().map(|x| alphabet[x].clone()).collect()
}

Однако это не в состоянии генерировать итеративно - оно проходит через целоебеспорядок рекурсии и итерации, который невозможно распутать в одно состояние.

Ответы [ 2 ]

0 голосов
/ 05 июля 2019

Рассмотрим этот подход:

  1. Выберите первого (лексикографически) представителя из каждого класса ожерелий

    Вот код Python для генерации представителей для ( бинарных ) ожерелий, содержащих d (можно повторить для всех значений d). Савада ссылка на статью

  2. Сортировка представителей в лексикографическом порядке

  3. Делать периодическое сокращение для каждого представителя (если это возможно): если строка периодическая s = p^m, как 010101, выберите 01

    Чтобы найти период, можно использовать удвоение строки или z-алгоритм (я полагаю, это быстрее для скомпилированных языков)

  4. Сокращения сцепления

    Пример для n = 3, k = 2:
    Сортировка представителей: 000, 001, 011, 111
    Сокращения: 0, 001, 011, 1
    Результат: 00010111

Тот же существенный метод (с кодом C) описан в книге Йорга Арндта "Вопросы вычислений" , глава 18

Подобный способ упоминается в вики

Альтернативная конструкция предполагает объединение вместе, в лексикографический порядок, все слова Линдона, длина которых делится n

Вы можете найти эффективный способ генерировать подходящие слова Линдона

0 голосов
/ 05 июля 2019

Я не знаком с Rust, поэтому я запрограммировал и протестировал его на Python. Поскольку постер перевел версию вопроса из программы на Python, я надеюсь, что это не будет большой проблемой.

# the following function treats list a as
# k-adic number with n digtis
# and increments this number returning
# the index of the leftmost digit changed
def increment_a7(a, k, n):
    digit= n-1
    a[digit]+= 1
    while a[digit] >= k and digit> 0:
        #a[digit]= 0
        a[digit]= a[0]+1
        a[digit-1]+= 1
        digit-= 1
    return digit

# the following function adds a to the sequence
# and takes into account, that the beginning of a
# could overlap with the end of sequence
# in that case, it just removes the overlapping digits
# from a before adding the remaining digits to sequence
def append_to_sequence(sequence, a, n):
    # here we can assume safely, that a
    # does not overlap completely with sequence[-n:]
    i= -1
    for i in range(n-1, -1, -1):
        found= True
        # check if the last i digits in sequence
        # overlap with the first i digits in a
        for j in range(i):
            if a[j] != sequence[-i+j]:
                # no, they don't overlap
                found= False
                break
        if found:
            # yes they overlap, so no need to 
            # continue the check with a smaller i
            break
    # now we can just append everything from
    # digit i (digit 0 - i-1 are swallowed)
    sequence.extend(a[i:])    
    return n-i

# during the operation we have to keep track of
# the k-adic numbers a, that already occured in
# the sequence. We store them in a set called used
# everytime we add something to the sequence
# we have to update it and add one entry for each
# digit inserted
def update_used(sequence, used, n, num_inserted):
    l= len(sequence)
    for i in range(num_inserted):
        used.add(tuple(sequence[-n-i:l-i]))

# the main work is done in the following function
# it creates and returns the generated sequence
def gen4(k, n):
    a= [0]*n
    sequence= a[:]
    used= set()
    # create a fake sequence to add the segments obtained by the cyclic nature
    fake= ([k-1] * (n-1))
    for i in range(n-1):
        fake.append(0)
        update_used(fake, used, n, 1)
    update_used(sequence, used, n, 1)
    valid= True
    while valid:
        # a is still a valid k-adic number
        # this means the generation process
        # has not ended
        # so construct a new number from the n-1
        # last digits of sequence
        # followed by a zero
        a= sequence[-n+1:]
        a.append(0)
        while valid and tuple(a) in used:
            # the constructed k-adict number a
            # was already used, so increment it
            # and try again
            increment_a(a, k, n)
            valid= a[0]<k
        if valid:
            # great, the number is still valid
            # and is not jet part of the sequence
            # so add it after removing the overlapping
            # digits and update the set with the segments
            # we already used
            num_inserted= append_to_sequence(sequence, a, n)
            update_used(sequence, used, n, num_inserted)
    return sequence

Я протестировал приведенный выше код, сгенерировав несколько последовательностей с исходной версией gen и этой, используя те же параметры. Для всех наборов параметров, которые я тестировал, результат был одинаковым в обеих версиях.

Обратите внимание, что этот код менее эффективен, чем оригинальная версия, особенно если последовательность становится длинной. Я предполагаю, что затраты на операции над множествами оказывают нелинейное влияние на время выполнения.

Если хотите, вы можете улучшить его, например, используя более эффективный способ хранения используемых сегментов. Вместо того, чтобы работать с k-адическим представлением (a-list), вы можете использовать вместо этого многомерный массив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...