Питон, 183
def S(n):
b,c,e=16,'x+=x\n','x+=y\n';s=d='y+=x\n';a=i=0
if n<2:return
while~n&1:n>>=1;a+=1
while n:n>>=1;s+=[e,c][i]+d*(n&1);i=1;b-=1
while a:s+=[c,c*b+e*2][i];i=0;a-=1
print(s)
Я не могу гарантировать, что это останется в пределах 2х оптимальной программы для четных чисел, но она эффективна. Для всех допустимых входных данных 0 <= n < 65536
это по сути мгновенно и генерирует программу максимум из 33 инструкций. Для произвольного размера регистра n
(после фиксации этой константы) на это потребуется O(n)
не более 2n+1
инструкций.
Некоторая бинарная логика
Любое нечетное число n
может быть достигнуто в течение 31 шагов: делать y+=x
, получать x,y = 1,1
, а затем продолжает удваивая x
с x+=x
(для первого удвоения сделать x+=y
, так как x
нечетно , чтобы начать с). x
таким образом достигнет каждой степени 2 (это просто сдвиг влево), поэтому вы можете установить любой бит y
равным 1, добавив соответствующую степень 2. Поскольку мы используем 16-битные регистры и каждый бит, кроме для первого требуется одно удвоение для достижения и одно y+=x
для установки, мы получаем максимум 31 опс.
Любое четное число n
- это просто некоторая степень 2, назовите его a
, умножьте на нечетное число, назовите его m
; то есть n = 2^a * m
или эквивалентно n = m << a
. Используйте описанный выше процесс, чтобы получить m
, затем сбросьте x
его, сдвинув влево до тех пор, пока не станет 0. Сделайте a, x+=y
чтобы установить x = m
, а затем продолжайте удваивать x
, используя в первый раз x+=y
и затем используя x+=x
.
Что бы там ни было a
, требуется 16-a
сдвиг, x
чтобы получить y=m
и дополнительные a
сдвиги, чтобы сбросить x=0
. Другие a
сдвиги x
произойдут после x=m
. Таким образом, общее количество 16+a
смен используется. Есть до 16-a
битов, которые нужно установить, чтобы получить m
, и каждый из них будет по одному y+=x
. Наконец, нам нужен дополнительный шаг, x=0
чтобы установить его в m x+=y
. Таким образом, для получения любого четного числа требуется не более 33 шагов.
Конечно, вы можете обобщить это на регистр любого размера, и в этом случае он всегда принимает самое большее 2n-1
и 2n+1
ops для нечетных и четных n
битов соответственно.
Оптимальность
Этот алгоритм производит программу, близкие к оптимальному (т.е. в пределах , 2n+2
если n
минимальное количество шагов) для нечетных чисел. Для заданного нечетного числа n
, если m
th-й бит является первым 1, то любая программа предпринимает как минимум m
шаги, чтобы добраться до x=n
или y=n
, поскольку операция, которая увеличивает значения регистров быстрее всего,x+=x
или y+=y
(т.е. удваивается), и m
для получения 1- m
й бит из 1. Поскольку этот алгоритм выполняет не более 2m
одного шага (не более двух на удвоение, одно для сдвига и одно y+=x
), любое нечетное число представляется почти оптимальным.
Четные числа не так хороши, так как он всегда использует 16 операций для сброса, x
прежде чем что-либо еще, а 8, например, может быть достигнуто за 5 шагов.
Интересно, что приведенный выше алгоритм никогда не использует y+=y
вообще, так y
как всегда остается нечетным. В этом случае он может найти самую короткую программу для ограниченного набора, состоящего только из 3 операций.
тестирование
# Do an exhaustive breadth-first search to find the shortest program for
# each valid input
def bfs():
d = {(0,1):0}
k = 0xFFFF
s = set(range(k+1))
current = [(0,1)]
nexts = []
def add(pt, dist, n):
if pt in d: return
d[pt] = dist
s.difference_update(pt)
n.append(pt)
i = 0
while len(s) > 0:
i += 1
for p in current:
x,y = p
add((x,x+y&k), i, nexts)
add((y,x+y&k), i, nexts)
if y%2 == 0: add(tuple(sorted((x,y+y&k))), i, nexts)
if x%2 == 0: add(tuple(sorted((x+x&k,y))), i, nexts)
current = nexts
nexts = []
print(len(d),len(s))
# Mine (@rationalis)
def S(n):
b,c,e=16,'x+=x\n','x+=y\n';s=d='y+=x\n';a=i=0
if n<2:return ''
while~n&1:n>>=1;a+=1
while n:n>>=1;s+=[e,c][i]+d*(n&1);i=1;b-=1
while a:s+=[c,c*b+e*2][i];i=0;a-=1
return s
# @CChak's approach
def U(i):
if i<1:return ''
return U(i//2)+'y+=y\n' if i%4==0 else U(i-1)+'y+=x\n'
# Use mine on odd numbers and @CChak's on even numbers
def V(i):
return S(i) if i % 2 == 1 else U(i)
# Simulate a program in the hypothetical machine language
def T(s):
x,y = 1,0
for l in s.split():
if l == 'x+=x':
if x % 2 == 1: return 1,0
x += x
elif l == 'y+=y':
if y % 2 == 1: return 1,0
y += y
elif l == 'x+=y': x += y
elif l == 'y+=x': y += x
x %= 1<<16
y %= 1<<16
return x,y
# Test a solution on all values 0 to 65535 inclusive
# Max op limit only for my own solution
def test(f):
max_ops = 33 if f==S else 1000
for i in range(1<<16):
s = f(i); t = T(s)
if i not in t or len(s)//5 > max_ops:
print(s,i,t)
break
# Compare two solutions
def test2(f,g):
lf = [len(f(i)) for i in range(2,1<<16)]
lg = [len(g(i)) for i in range(2,1<<16)]
l = [lf[i]/lg[i] for i in range(len(lf))]
print(sum(l)/len(l))
print(sum(lf)/sum(lg))
# Test by default if script is executed
def main():
test()
if __name__ == '__main__':
main()
Я написал простой тест, чтобы проверить, что мое решение действительно дает правильные результаты и никогда не проходит 33 шага для всех допустимых входных данных (0 <= n < 65536
).
Кроме того, я попытался провести эмпирический анализ, чтобы сравнить выходные данные моего решения с оптимальными выходными данными - однако оказывается, что поиск в ширину слишком неэффективен, чтобы получить минимальную длину выходных данных для каждого действительного ввода n
. Например, использование BFS для поиска выходных данных n = 65535
не завершается в разумные сроки. Тем не менее я ушел bfs()
и открыт для предложений.
Тем не менее, я протестировал свое собственное решение с помощью @ CChak (реализовано здесь на Python как U
). Я ожидал, что мой будет работать хуже, так как он намного неэффективен для меньших четных чисел, но усреднен по всему диапазону двумя способами, при этом добыча составила в среднем 10,8% до 12,3% короче. Я подумал, что это может быть связано с большей эффективностью моего собственного решения для нечетных чисел, поэтому V
использует мое для нечетных чисел и @ CChak для четных чисел, но V
находится между ними (примерно на 10% короче, чем на U
3% длиннее S
).
x+=x
законно, только еслиx
это даже? Также для самой короткой программы, я думаю, что-то вроде BFS может работать.