Art arda 2 sayı ekleyerek herhangi bir sayı yapın


14

Size iki 16 bit kayıtlı bir makine verilir xve y. Kayıtlar başlatılır x=1ve y=0. Makinenin yapabileceği tek işlem, modulo 65536 toplama işlemidir.

  • x+=y- xile değiştirilir (x + y) mod 65536; ydeğişmedi
  • y+=x - benzer şekilde y
  • x+=x- xile değiştirilir 2x mod 65536; yasal xolsa bile
  • y+=y - benzer şekilde y

Amaç, kayıtlardan birinde ( xveya y) önceden belirlenmiş bir sayı elde etmektir .

Bir sayı (in stdin,, argvfonksiyon parametresi, yığının üst kısmı veya başka herhangi bir geleneksel yer) alan bir program veya alt yordam yazın ve bu sayıyı almak için bir program çıktısı alın. Çıktı stdout, stdoutbaşka herhangi bir geleneksel çıktı aygıtına gitmeli ya da (dilinizde yoksa ) olmalıdır.

Çıktı programı% 100'e kadar artı optimalden 2 adım uzakta olabilir. Yani, hedef numarayı elde etmek için en kısa programın nadımları varsa, çözümünüz bundan daha uzun olamaz 2n+2. Bu kısıtlama "çok kolay" çözümlerden kaçınmaktır (örneğin 1, 2, 3, ... saymak) ancak tam optimizasyon gerektirmez; En kısa programın bulunması en kolay olduğunu düşünüyorum, ancak emin olamıyorum ...

Örneğin: Giriş = 25. Çıkış:

y+=x
x+=y
x+=y
x+=x
x+=x
x+=x
y+=x

Başka bir örnek: Herhangi bir fibonacci numarası için, çıkışın bu alternatif modeli vardır. Giriş = 21 için çıkış

y+=x
x+=y
y+=x
x+=y
y+=x
x+=y
y+=x

En kısa kod (bayt cinsinden ölçülür) kazanır.

(Bu bulmaca, son zamanlarda üretmem gereken 16 bit işlemci için bazı kodlardan ilham aldı)

PS: merak ediyorum - hangi sayı için en uygun program en uzun?


9
Meraktan dolayı, neden x+=xsadece olsa xbile yasaldır ? Ayrıca en kısa program için BFS gibi bir şeyin işe yarayabileceğini düşünüyorum.
Sp3000

Hedefe ulaştıktan sonra, bir sonraki hedef sayıya devam etmek isteyebilir; herhangi bir hedefe ulaşma imkanına sahip olmak için, sayılardan birinin tuhaf olması gerekir. Gereksiz karmaşıklığı önlemek için sonsuz bir hedef akışı yapmak istemedim, ama ruh böyle bir akıma izin vermektir ...
anatolyg

Adım sayısı kısıtlamasını değiştirdim, bu nedenle hedef sayı 0 veya 1 için çıktı programının boş olması gerekmez.
anatolyg

3
eğer x+=xsadece bile çalışır xs, nasıl 25 çiftlerde 3 bir girişi için örnek geldin?
bcsb1001

Yanıtlar:


2

CJam, 31

Gibi @Tobia 'ın cevabı, benim algoritması da utanmadan edilir çalınmış esinlenerek @CChak ler yanıt'. Ancak, CJam olan kara büyüyü kullanarak, algoritmanın daha küçük bir uygulamasını yapmayı başardım.

Burada deneyin.

golfed:

qi{_4%!:X)/X!-'xX+"
y+="@}h;]W%`

Ungolfed:

qi          "Read input and convert to integer.";
{           "Do...";
  _4%!:X    "Assign (value mod 4 == 0) to X.";
  )/X!-     "If X, divide value by 2. If not X, decrement value.";
  'xX+      "If X, put 'y' on the stack. If not X, put 'x' on the stack.";
  "
y+="        "Put '\ny+=' on the stack.";
  @         "Rotate top 3 elements of stack left so the value is on top.";
}h          "... while value is not zero.";
;           "Discard zero value on stack.";
]W%         "Collect stack into array and reverse.";

Yanılıyorsam lütfen beni düzeltin, ancak benzer bir algoritmaya sahip cevaplarda kullanılan modulo 65536 işleminin gerekli olmadığına inandım. Soruyu, girdinin geçerli bir işaretsiz 16 bit tam sayı olacağını ve bu algoritmanın herhangi bir ara değeri veya sonucunun da olacağını varsayabilecek şekilde yorumladım.


Mod 65536'nın kaldırılmasıyla ilgili ilginç bir nokta. "Önceden belirlenmiş sayının" 0-65535 içinde olması gerektiği iyi bir durum olduğunu düşünüyorum.
CChak

8

Perl 107 97

İlk gönderi, işte gidiyor.

sub h{($i)=@_;return if(($i%=65536)==0);($i%4==0)?do{h($i/2);say"y+=y";}:do{h($i-1);say"y+=x";}}

Tüm kayıt ekleme kriterlerine uyuyor, ancak cevabımın her zaman en uygun adım sayısının 2n + 2'si içinde olup olmadığını görmek için kapsamlı bir kontrol yapmadım. Yine de, her Fibonacci sayısı için sınırın içindedir.

İşte daha ayrıntılı bir döküm

sub h{                           # Declare the subroutine, it should be called referencing an integer value
   ($i)=@_;                      # Assign the i variable to the integer used in the call
   return if(($i%=65536)==0);    # Check for base condition of called by 0, and enforce modulo from the start.
   ($i%4==0) ?                   # If the value passed is even, and will result in an even number if we halve it
   do{h($i/2);say"y+=y";}        # Then do so and recurse 
   :do{h($i-1);say"y+=x";}       # Otherwise "subtract one" and recurse
}                                # Note that the print statements get called in reverse order as we exit.

Bahsettiğim gibi, bu benim ilk golf girişimim, bu yüzden bu geliştirilebilir eminim. Ayrıca, ilk altyordam çağrısının yinelemeli bir çağrıda sayılması gerekip gerekmediğinden emin değilim, bu da bizi birkaç karakter yukarı çekebilir.

İlginç bir şekilde kodu 11 bayt * azaltabilir ve kayıt değerlerinin sayısı açısından "verimliliğimizi" artırabiliriz. Bunu eğlence için buraya ekledim:

sub h{($i)=@_;return if(($i%=65536)==0);($i%2==0)?do{h($i/2);say"y+=y";}:do{h($i-1);say"y+=x";}}

Zeyilname başlat:

Bu sorunu gerçekten beğendim ve son birkaç haftadır bununla uğraşıyorum. Sonuçlarımı göndereceğimi düşündüm.

Bazı sayılar:

Optimal bir çözüm bulmak için bir BFS algoritması kullanarak, ilk 2 ^ 16 rakamlarında 23 adım gerektiren sadece 18 sayı vardır. Bunlar: 58558, 59894, 60110, 61182, 61278, 62295, 62430, 62910, 63422, 63462, 63979, 64230, 64314, 4486, 64510, 64698, 64854, 65295.

Yukarıda açıklanan özyinelemeli algoritmayı kullanarak, ulaşılması gereken "en zor" sayı 45 işlemde 65535'tir. (65534 44 alır ve 43 adım alan 14 sayı vardır) 65535 aynı zamanda en uygun, 45'e 22 olan en büyük kalkıştır. 23 adım farkı 2n + 1'dir. (Sadece üç sayı 2n'ye ulaştı: 65534, 32767, 32751.) Önemsiz (sıfır adım) durumlar hariç, tanımlanan aralıkta, özyinelemeli yöntem en uygun çözümün yaklaşık 1.4 katıdır.

Alt satır: 1-2 ^ 16 sayıları için, özyinelemeli algoritma asla tanımlanan 2n + 2 eşiğini geçmez, bu nedenle cevap geçerlidir. Bununla birlikte, daha büyük kayıtlar / daha fazla bit için en uygun çözümden çok uzaklaşmaya başlayacağından şüpheleniyorum.

BFS oluşturmak için kullandığım kod özensiz, bellek yoğun, yorum yapılmamış ve oldukça kasıtlı olarak dahil değildi. Yani ... sonuçlarıma güvenmek zorunda değilsin, ama onlara çok güveniyorum.


BFS olmayan bir çözüm, harika!
anatolyg

En patolojik vakalar için bile 4, belki daha iyi bir faktör içinde kalacağınızı düşünüyorum (çünkü en uygun çözüm için sadece bir alt sınır bildiğim için). Bu hala oldukça iyi.
rationalis

7

Python 3, 202 bayt

def S(n):
 q=[(1,0,"")];k=65536
 while q:
  x,y,z=q.pop(0)
  if n in{x,y}:print(z);return
  q+=[((x+y)%k,y,z+"x+=y\n"),(x,(x+y)%k,z+"y+=x\n")]+[(2*x%k,y,z+"x+=x\n")]*(~x&1)+[(x,2*y%k,z+"y+=y\n")]*(~y&1)

(@Rationalis'e birkaç bayt için teşekkürler)

İşte son derece basit bir çözüm. Keşke son çizgiyi daha iyi golf yapabilsem, ama şu anda fikirlerim bitiyor. İle arayın S(25).

Program sadece önbellekleme olmadan basit bir BFS yapar, bu yüzden çok yavaş. İşte, S(97)bazı örnek çıktılar için:

y+=x
x+=y
x+=x
x+=x
x+=x
x+=x
y+=x
y+=x
x+=y

5

Dyalog APL, 49 karakter / bayt *

{0=N←⍵|⍨2*16:⍬⋄0=4|N:⎕←'y+=y'⊣∇N÷2⋄⎕←'y+=x'⊣∇N-1}

@CChak'ın cevabından utanmadan esinlenen algoritma .

Misal:

    {0=N←⍵|⍨2*16:⍬⋄0=4|N:⎕←'y+=y'⊣∇N÷2⋄⎕←'y+=x'⊣∇N-1} 25
y+=x
y+=x
y+=y
y+=x
y+=x
y+=y
y+=y
y+=x

Ungolfed:

{
    N←(2*16)|⍵                 # assign the argument modulo 65536 to N
    0=N: ⍬                     # if N = 0, return an empty value (that's a Zilde, not a 0)
    0=4|N: ⎕←'y+=y' ⊣ ∇N÷2     # if N mod 4 = 0, recurse with N÷2 and *then* print 'y+=y'
    ⎕←'y+=x' ⊣ ∇N-1            # otherwise, recurse with N-1 and *then* print 'y+=x'
}

* Dyalog APL, üst 128 bayt değerlerine eşlenmiş APL sembolleri bulunan eski bir karakter kümesini destekler. Bu nedenle, yalnızca ASCII karakterleri ve APL sembolleri kullanan bir APL programı bytes == chars olarak kabul edilebilir.


3

Python, 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)

Bunun çift sayılar için en uygun programın 2 katı içinde kalmasını garanti edemem, ancak etkilidir. Tüm geçerli girdiler için 0 <= n < 65536, esasen anlıktır ve en fazla 33 talimatdan oluşan bir program oluşturur. Rastgele bir kayıt boyutu için n(bu sabiti düzelttikten sonra), O(n)en fazla 2n+1talimatla zaman alacaktır .

Bazı İkili Mantık

Herhangi tek sayı n31 adımda ulaşılabilir: do y+=x, alma x,y = 1,1ve daha sonra iki katına tutmak xile x+=x(ilk iki katına yapmak x+=yberi xbaşlamak için garip). x2'nin her gücüne bu şekilde ulaşacaktır (sadece bir sola kaydırma) ve böylece y2'nin ilgili gücünü ekleyerek 1 olacak şekilde herhangi bir biti ayarlayabilirsiniz. 16 bitlik kayıtlar kullandığımız ve birincisi ulaşmak için bir katlama ve bir tane y+=xayarlamak için en fazla 31 ops alırız.

Herhangi bir çift sayı nsadece 2'nin bir gücüdür a, diyelim, tek bir sayı, deyin m; yani n = 2^a * mveya eşdeğer olarak n = m << a. Almak için yukarıdaki işlemi kullanın msonra sıfırlama, xbu 0. yapmak var kadar sol kaydırarak x+=ysetine x = mve daha sonra çift devam x, ilk kez kullanan x+=ykullanarak sonradan ve x+=x.

Ne olursa olsun aTek gereken, olduğu 16-abir vardiya xalmak y=mve ek avardiya sıfırlamak için x=0. Sonra başka bir akayma xgerçekleşecek x=m. Böylece toplam 16+avardiya kullanılır. 16-aAlmak için ayarlanması gereken en fazla bit vardır mve bunların her biri bir tane alacaktır y+=x. Sonunda ne zaman x=0m'ye ayarlayacağımız fazladan bir adıma ihtiyacımız var x+=y. Bu nedenle, herhangi bir çift sayı elde etmek için en fazla 33 adım gerekir.

Elbette, bunu herhangi bir boyut kaydına genelleştirebilirsiniz, bu durumda sırasıyla en fazla zaman alır 2n-1ve 2n+1tek ve çift nbitli tamsayıları seçer.

Optimalliği

Bu algoritma (içinde, yani yakın optimal olan bir program üretir 2n+2ise ntek sayı için adımların en az bir sayı). Belirli bir tek sayı için nise, minci biraz 1 gelen, daha sonra herhangi bir program, en azından alır madımları almak için x=nya da y=n, hızlı sicil değerlerini arttırır olduğu işlem yana x+=xya da y+=y(örneğin, ikiye katlama) ve alır malmak misli mBu algoritma yana 1'den inci bit en alır 2madımları (en fazla iki kat başına iki, geçiş için ve bir y+=xherhangi bir tek sayı yakın en uygun şekilde temsil edilir).

Rakamlar bile o kadar iyi değildir, çünkü xher şeyden önce sıfırlamak için her zaman 16 op kullanır ve örneğin 8'e 5 adımda ulaşılabilir.

İlginçtir, yukarıdaki algoritma asla hiç kullanmaz y+=y, çünkü yher zaman gariptir. Bu durumda, sadece 3 işlemin kısıtlı seti için en kısa programı bulabilir.

Test yapmak

# 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()

Çözümümün gerçekten doğru sonuçlar verdiğini ve tüm geçerli girdiler için hiçbir zaman 33 adımı geçmediğini kontrol etmek için basit bir test yazdım 0 <= n < 65536.

Buna ek olarak, çözümümün çıktısını optimum çıktılarla karşılaştırmak için ampirik bir analiz yapmaya çalıştım - ancak, ilk genişlik aramasının her geçerli giriş için minimum çıktı uzunluğunu elde etmek için çok verimsiz olduğu ortaya çıktı n. Örneğin, çıktıyı bulmak için BFS kullanımı n = 65535makul bir süre içinde sona ermez. Yine de bıraktım bfs()ve önerilere açıkım.

Ancak, kendi çözümümü @ CChak'lara karşı test ettim (burada Python'da uygulandı U). Benimkinin daha kötü olmasını bekliyordum, çünkü daha küçük çiftler için büyük ölçüde verimsiz, ancak tüm aralıkta iki şekilde ortalama, benimki ortalama üretimin ortalama% 10.8 ila% 12.3 daha kısa üretti. Belki de bunun tek sayılarda kendi Vçözümümden daha iyi verimden kaynaklandığını düşündüm, bu yüzden tek sayılarda mayın ve çift sayılarda @ CChak kullanıyor, ancak Varasında (yaklaşık% 10 daha kısa U,% 3 daha uzun S).


1
201 baytta oldukça fazla mantık!
anatolyg

@analtolyg Ne diyebilirim ki, matematik ve biraz uğraşmayı severim. Diğer yaklaşımları araştırabilirim, çünkü çift sayı çözümünün iyileştirilmesi gereken bir yer vardır.
rationalis

Vay, x,y='xy'şimdiye kadar mümkün olduğunu bile bilmiyordum . Ne yazık ki, biçimlendirme c*b+e*2ile kısaca yeniden yazmanın bir yolunu düşünemiyorum %.
rationalis

Ah başka bir yerde kullandığını fark etmedim. Sadece ben miyim yoksa S(2)çıktısı gerçekten uzun mu?
Sp3000

Ne yazık ki, benim çözümümle, her çift sayı en az 19 adım atıyor (19'da S(2)en kısa olanı). İzlemiyorum xve yaçıkça, bu yüzden xikinci adımdan sonra 2'ye ulaşmasına rağmen , yine de sıfırlamaya devam ediyor. xSanki daha iyi bir çözüm olmalı gibi hissediyorum, ama henüz bir.
rationalis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.