Phinary Numarasını Standartlaştırın


32

Arka fon

Buradaki çoğu insan birkaç tamsayılı temel sistemlere aşina olmalıdır: ondalık, ikili, onaltılık, sekizli. Örneğin onaltılık sistemde, bir abc.de 16 sayısı temsil eder

a*16^2 + b*16^1 + c*16^0 + d*16^-1 + e*16^-2

Bununla birlikte, biri irrasyonel sayılar gibi tamsayılı olmayan tabanları da kullanabilir. Bu tür temel altın oranı kullanan bir kez φ = (+ √5 1) / 2 ≈ 1.618 ... . Bunlar tamsayı bazlarına benzer şekilde tanımlanmıştır. Bir numara Yani abc.de cp (burada bir karşı e basamak tamsayı vardır) temsil edecek

a*φ^2 + b*φ^1 + c*φ^0 + d*φ^-1 + e*φ^-2

Prensipte, herhangi bir basamağın negatif olabileceğine dikkat edin (buna alışkın olmasak da) - bir basamağı olan negatif basamağı temsil edeceğiz ~. Biz rakamlarından kendimizi kısıtlamak Bu sorunun amacına uygun ~9üzere 9, bu yüzden açık bir şekilde (aralarında yaklaşık işaretli) bir dize olarak bir numara yazabilirsiniz. Yani

-2*φ^2 + 9*φ^1 + 0*φ^0 + -4*φ^-1 + 3*φ^-2

olarak yazılmış olur ~290.~43. Böyle bir numaraya phinary numarası diyoruz .

Bir phinary sayısı her zaman temsil edilebilir standart form temsil sadece basamak kullanan araçlar, 1ve 0içeren olmadan 11herhangi bir yerde ve isteğe bağlı bir eksi işareti ile tüm sayısı negatif olduğunu gösterir. (İlginçtir ki, her tamsayı standart biçimde benzersiz bir sonlu gösterime sahiptir.)

Standart biçimde olmayan temsiller, aşağıdaki gözlemler kullanılarak her zaman standart forma dönüştürülebilir:

  1. 011 φ = 100 φ (çünkü φ 2 = φ + 1)
  2. 0200 φ = 1001 φ (çünkü φ 2 + 1 / φ = 2φ)
  3. 0 ~ 10 φ = ~ 101 φ (çünkü φ - 1 / φ = 1)

Ek olarak:

  1. En önemli basamak ise ~1(sayının geri kalanı standart formdaysa ), sayı negatiftir ve hepsini değiştirerek 1ve ~1eksi işareti hazırlayarak ve yukarıdaki üç kuralı tekrar uygulayarak standart forma dönüştürebiliriz. standart formu elde edin.

İşte böyle bir normalizasyon örneği: ( her basamak konumunu hizalı tutmak için pozitif basamaklar için ek boşluklar kullanıyorum): 1~3.2~1φ

      1~3. 2~1φ         Rule:
=     0~2. 3~1φ         (3)
=    ~1~1. 4~1φ         (3)
=  ~1 0 0. 4~1φ         (3)
=  ~1 0 0. 3 0 1φ       (3)
=  ~1 0 1. 1 0 2φ       (2)
=  ~1 1 0. 0 0 2φ       (1)
=  ~1 1 0. 0 1 0 0 1φ   (2)
= - 1~1 0. 0~1 0 0~1φ   (4)
= - 0 0 1. 0~1 0 0~1φ   (3)
= - 0 0 1.~1 0 1 0~1φ   (3)
= - 0 0 0. 0 1 1 0~1φ   (3)
= - 0 0 0. 0 1 1~1 0 1φ (3)
= - 0 0 0. 0 1 0 0 1 1φ (3)
= - 0 0 0. 0 1 0 1 0 0φ (1)

Verim .-0.0101φ

Daha fazla okumak için Wikipedia'nın konuyla ilgili çok bilgilendirici bir makalesi var.

Meydan okuma

Bu nedenle, ya da başka türlü, (yukarıda açıklandığı gibi) bir phinary sayısını temsil eden bir dize verildiğinde, sıfır ya da son sıfır olmadan, standart biçimini veren bir program ya da işlev yazın. Girdi, mutlaka kritik noktayı içermez, ancak her zaman solundaki basamağı içerecektir (yani hayır .123). Çıktı her zaman phinary noktasını ve solundaki en az bir rakam içermelidir.

STDIN, ARGV veya function argümanı ile giriş yapabilir ve sonucu geri döndürebilir veya STDOUT'a yazdırabilirsiniz.

Prensipte doğru ve kesin (geçerli) girdiler için kesin olduğu sürece yukarıdaki prosedürden farklı bir algoritma kullanabilirsiniz - yani, uygulamanızı potansiyel olarak kırabilecek tek sınırlamalar, yerleşiklerin boyutu gibi teknik sınırlamalar olmalıdır. veri türleri veya kullanılabilir RAM Örneğin, girişi kayan nokta sayısı olarak değerlendirmek ve daha sonra yavaşça rakamları seçmek yasaktır; zira kayan nokta hatalarının yanlış sonuçlara yol açabileceği girdiler bulunabilir.

Bu kod golf, en kısa cevap (bayt cinsinden) kazanır.

Test Kılıfları

Input       Output

1           1.
9           10010.0101
1.618       10000.0000101
1~3.2~1     -0.0101
0.~1021     0. (or -0.)
105.~2      1010.0101
~31~5.~1    -100000.1001

Şimdi numaralarımda negatif rakamlar kullanmak istiyorum! 1 ~ 3 * 6 == 5 ~ 8
Aaron

Yanıtlar:


6

Javascript (ES6) - 446 418 422 420 bayt

minified:

F=s=>{D=[];z='000000000';N=t=n=i=e=0;s=(z+s.replace(/^([^.]*)$/,'$1.')+z).replace(/~/g,'-').replace(/-?\d/g,s=>((D[n++]=s/1),0));for(;i<n-3;i=j){if(p=D[j=i+1]){if(!e&&p<0){D=D.map(k=>-k);N=~N;p=-p}e=1}d=D[i];x=D[i+2];m=D[i+3];if(p<0){d--;p++;x++;e=j=0}if(p>1){d++;m++;p-=2;e=j=0}if(!d&&p*x==1){d=p;e=j=p=x=0}D[i]=d;D[i+1]=p;D[i+2]=x;D[i+3]=m}return(N?'-':'')+s.replace(/0/g,()=>D[t++]).replace(/^(0(?!\.))+|0+$/g,'')}

Expanded:

F = s => {
    D = [];
    z = '000000000';
    N = t = n = i = e = 0;
    s = (z + s.replace( /^([^.]*)$/, '$1.' ) + z).replace( /~/g, '-' ).
        replace( /-?\d/g, s => ((D[n++]=s/1),0) );

    for( ; i < n-3; i = j ) {
        if( p = D[j = i+1] ) {
            if( !e && p < 0 ) {
                D = D.map( k=>-k );
                N = ~N;
                p = -p;
            }
            e = 1;
        }
        d = D[i];
        x = D[i+2];
        m = D[i+3];

        if( p < 0 ) {
            d--;
            p++;
            x++;
            e = j = 0;
        }
        if( p > 1 ) {
            d++;
            m++;
            p-=2;
            e = j = 0;
        }
        if( !d && p*x == 1 ) {
            d = p;
            e = j = p = x = 0;
        }

        D[i] = d;
        D[i+1] = p;
        D[i+2] = x;
        D[i+3] = m;
    }

    return (N ? '-' : '') + s.replace( /0/g, ()=>D[t++] ).replace( /^(0(?!\.))+|0+$/g, '' );
}

Kod Fbelirtilen dönüşümü gerçekleştiren bir işlev üretir .

Golf oynamak zor bir problem. Kodun basitleştirilmesini önleyen sayısız kenar durumu. Özellikle, negatiflerle başa çıkmak hem ayrıştırma hem de mantıksal kullanım açısından bir acıdır.

Kodun yalnızca girdilerin "makul bir aralık" ını taşıdığını not etmeliyim. İşlevin alanını sınırsız genişletmek için içerideki sıfırların sayısı zarttırılabilir ve sabit sınırlayıcı while( c++ < 99 )döngü artabilir. Şu anda desteklenen aralık, verilen test durumları için zaten fazladır.

Örnek çıktılar

F('1')          1.
F('9')          10010.0101
F('1~3.2~1')    -0.0101
F('0.~1021')    -0.
F('105.~2')     1010.0101
F('~31~5.~1')   -100000.1001

-0.Hoş değil, ama cevap hâlâ doğrudur. Gerekirse düzeltebilirim.


@ MartinBüttner: Yapabilirsin, ama zor olurdu. Tam giriş üzerindeki "geçiş" sayısını sınırlar ve her geçiş birkaç işlemden oluşur. Benim bağırsak hissediyorum geçiş sayısı herhangi normalleştirmek için gerekli olmasıdır n-digit girdi arasında bir yerde olurdu nve n log(n). Her durumda, geçiş sayısı eklenen her karakter için 10 kat artırılabilir. Sabitteki sıfır sayısı zda ilginç bir sorundur. Ben 9 için overkill şüpheli herhangi olası girdi.
COTO

@ MartinBüttner: Teşekkürler. Karakter sınıfındaki kaçışları kaldırdım. $0Javascript gelince , onu desteklemiyor. Ya da en azından Firefox yok. : P
COTO

Tamam, bence tampon olarak 7'den fazla öncü sıfıra asla ihtiyaç duymuyorsunuz, ama takip eden sıfırları tahmin etmenin biraz zor olacağını düşünüyorum. Dış döngüye gelince, sadece bir süre döngü yaparsanız (veya bunu döngü için içsel ile birleştirirseniz) ve başka değişiklik olmadığı zaman patlarsanız, buna ihtiyacınız bile olmadığını sanmıyorum. Sanırım spekülasyonum bu konuda biraz daha net olabilirdi ama "ilke olarak keyfi (geçerli) girdiler için doğru ve kesin" derken, yalnızca teorik sınırın dahili veri tiplerinizin / RAM'inizin boyutu olması gerektiğini kastettim.
Martin Ender

1
@COTO için 1 byte kaydetmek, sen ilk kısmını taşımayı deneyebilirsiniz for( i = e = 0; i < n-3; i = j )tarafından for(; i < n-3; i = j )ve üstüne beyanları hareket ediliyor N = t = n = 0;ile değiştirilirN = t = n = i = e = 0;
Ismael Miguel

1
@ IsmaelMiguel: jdeğerinde sabit tutulmaz i+1. Üç ifbloktaki ihbar j, sıfırlanır 0. Dolayısıyla ilk ifbloktan sonra herhangi bir noktada bir vekil olarak kullanılamaz i+1. Değişkenin ikendisi, döngünün sonuna kadar (üçüncü deyimi kullanarak for) değeri, döngünün sonuna kadar kullanıldığından beri güncellenemez. Ama bunu söyledikten sonra belki bir şeyleri özlüyorum. Kodu kısaltabilir, test edebilir ve hala çalıştığını doğrulayabilirseniz, lütfen pastebin.com'a bir kopya ve buraya bir bağlantı gönderin. Cevabınız için size vadesi gelmiş krediyi uzatacağım. :)
COTO

2

Haskell, 336 bayt

z=[0,0]
g[a,b]|a*b<0=g[b,a+b]
g x=x<z
k![a,b,c,d]=[b,a+b,d-c+read k,c]
p('.':s)=1:0:2`drop`p s
p('~':k:s)=['-',k]!p s
p(k:s)=[k]!p s
p[]=1:0:z
[1,0]&y='.':z?y
[a,b]&y=[b,a+b]?y
x@[a,b]?y@[c,d]|x==z,y==z=""|g y='-':x?[-c,-d]|g[c-1,d]='0':x&[d,c+d]|g[c,d-1]='1':x&[d,c+d-1]|0<1=[b-a,a]?[d-c,c]
m[a,b,c,d]=[1,0]?[a*d+b*c-a*c,a*c+b*d]
f=m.p

Bu açgözlü algoritmadır, ancak kayan nokta hatalarını önlemek için a + ( a , b ∈ ℤ) [a,b]sayılarını tam olarak gösterir . a + <0 olup olmadığını test eder. Kullanım örneği:g[a,b]

*Main> f "9"
"10010.0101"
*Main> f "1~3.2~1"
"-0.0101"
*Main> f "0.~1021"
"0."
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.