Engee 文档
Notebook

Julia语言中的加性链算法

这个例子讨论了在Julia编程语言中找到给定数字的加性链的最小长度的算法的实现。

导言

加性链算法解决了找到获得目标数所需的最小数量的步骤的问题,从一个开始,其中链的每个下一个成员等于同一链的两个先前成员的总和。

这是数论中的一个经典问题,在优化指数,密码学中的计算以及分析某些算法的复杂性方面都有应用。

理论部分

添加剂фепочка长度 它是一个序列 ,如 . 每个元素是前两个术语的总和,不一定可区分。

[布劳尔之链](https://en.wikipedia.org/wiki/Addition_chain#Brauer_chain "wp:加法链")用于 是一个加成链,其中 . 每个元素都使用前一个元素作为求和。

在这个例子中,我们对最小长度的链感兴趣。 .

任务

为所有人 输出以下内容:

  • ,

-Brower长度链的数量 .

功能 trypermutation

功能 trypermutation 递归地构建此类总和的所有可能序列,试图以最少的步骤数实现目标。 这是通过经历所有可能的组合(排列)来完成的。

主要部分

检查序列当前状态的功能 — checksequence

In [ ]:
"""
Проверяет условия завершения рекурсии или продолжает её.

pos      -- текущее количество шагов (глубина рекурсии),
seq      -- текущая цепочка значений,
n        -- целевое значение,
minlen   -- максимальная разрешённая глубина (минимально найденная длина до этого момента).

Возвращает кортеж из:
(minimal_length_found, number_of_ways_with_that_length)
"""
function checksequence(pos, seq, n, minlen)
    # Если мы уже превысили известный минимум – дальше не стоит проверять
    pos > minlen || seq[1] > n ? (minlen, 0) :

    # Если первое значение стало больше N – цепочка не годится
    seq[1] == n ? (pos, 1) : # Мы достигли цели! Возвращаем кол-во шагов и одну найденную цепочку

    # Если ещё не достигли максимума, но можно продолжать перебирать:
    pos < minlen ? trypermutation(0, pos, seq, n, minlen) : (minlen, 0) # Иначе всё равно перешагнули
end
Out[0]:
checksequence

尝试所有有效排列的递归函数 — trypermutation

In [ ]:
"""
trypermutation(i, pos, seq, n, minlen) 

i          -- индекс последнего элемента, который может быть слагаемым,
pos        -- сколько элементов есть в текущей цепочке (длина цепочки),
seq        -- сама цепочка чисел,
n          -- целевое (искомое) число,
minlen     -- текущая лучшая найденная длина цепочки.

Возвращает пару (лучшая_длина, число способов получить эту длину).
"""
function trypermutation(i, pos, seq, n, minlen)
    if i > pos
        return minlen, 0 # Ничего нового не нашлось – просто возвращаем минимум
    end
    
    new_seq = deepcopy(seq) # Создаём копию цепочки
    pushfirst!(new_seq, seq[1] + seq[i+1]) # Добавляем новое число в начало (слева): a[j] = a[1]+a[i+1]

    res1 = checksequence(pos + 1, new_seq, n, minlen) # Проверяем полученную цепочку дальше

    res2 = trypermutation(i + 1, pos, seq, n, res1[1]) # Пробуем другой вариант i -> i+1

    # Выбираем наилучший результат:
    if res2[1] < res1[1]
        return res2
    elseif res2[1] == res1[1]
        return res2[1], res1[2] + res2[2] # Если равно — складываем количества путей
    else
        error("Результат с большим i меньше результата меньшей i – логически ошибочно!")
    end
end
Out[0]:
trypermutation

主程序:输出一组数字的结果

一组测试数字:对于它们中的每一个,将找到添加剂链的最小长度。

In [ ]:
for num in [7, 14, 21, 29, 32, 42, 64, 47, 79, 191, 382, 379]
    (minlen, nchains) = trypermutation(0, 0, [1], num, 12) # Стартовая цепочка — [1]; максимальная допустимая длина — 12
    println("N = ", num)
    println("Минимальная длина цепочек: L(n) = ", minlen)
    println("Количество цепочек такой длины (число Брауэра): ", nchains)
end
N = 7
Минимальная длина цепочек: L(n) = 4
Количество цепочек такой длины (число Брауэра): 5
N = 14
Минимальная длина цепочек: L(n) = 5
Количество цепочек такой длины (число Брауэра): 14
N = 21
Минимальная длина цепочек: L(n) = 6
Количество цепочек такой длины (число Брауэра): 26
N = 29
Минимальная длина цепочек: L(n) = 7
Количество цепочек такой длины (число Брауэра): 114
N = 32
Минимальная длина цепочек: L(n) = 5
Количество цепочек такой длины (число Брауэра): 1
N = 42
Минимальная длина цепочек: L(n) = 7
Количество цепочек такой длины (число Брауэра): 78
N = 64
Минимальная длина цепочек: L(n) = 6
Количество цепочек такой длины (число Брауэра): 1
N = 47
Минимальная длина цепочек: L(n) = 8
Количество цепочек такой длины (число Брауэра): 183
N = 79
Минимальная длина цепочек: L(n) = 9
Количество цепочек такой длины (число Брауэра): 492
N = 191
Минимальная длина цепочек: L(n) = 11
Количество цепочек такой длины (число Брауэра): 7172
N = 382
Минимальная длина цепочек: L(n) = 11
Количество цепочек такой длины (число Брауэра): 4
N = 379
Минимальная длина цепочек: L(n) = 12
Количество цепочек такой длины (число Брауэра): 6583

主循环是如何工作的?

对于列表中的每个数字,执行以下操作:

  1. 开始递归迭代。 trypermutation,从起始链开始 [1].
  2. 递归通过添加一个新项作为任何两个现有项的总和来生成所有可能的序列。
  3. 功能 checksequence 跟踪期望值的实现(== n),如果新链比当前链短,则进行修复。
  4. 在迭代结束时,每个输入输出两个数字:
    • L(n) -添加剂链的最小长度;
      -最小Brower链的数量(具有不同的添加顺序,但长度相同)。

结论

我们已经回顾了在Julia环境中使用递归的全迭代方法构建最小加性链的算法的实现。 对于一组数字,找到相应链的最小长度,并计算此类链(所谓的Brower链)的数量。 这种技术例如对于用于快速指数化和分析密码系统中操作的复杂性的高效算法是有用的。

该示例是使用[罗塞塔代码]的材料开发的(https://rosettacode.org/wiki/Addition_chains