สร้างตัวเลขมาตรฐาน


32

พื้นหลัง

คนส่วนใหญ่ที่นี่ควรทำความคุ้นเคยกับระบบฐานจำนวนเต็มสองสามอย่าง: ทศนิยม, ไบนารี, ฐานสิบหก, ฐานแปด เช่นในระบบเลขฐานสิบหกตัวเลขabc.de 16จะแทน

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

อย่างไรก็ตามหนึ่งสามารถใช้ฐานที่ไม่ใช่จำนวนเต็มเช่นตัวเลขที่ไม่มีเหตุผล เมื่อใช้ฐานเช่นอัตราส่วนทองคำφ = (1 + √5) / 2 ≈ 1.618 ... สิ่งเหล่านี้ถูกกำหนดแบบอะนาล็อกกับฐานจำนวนเต็ม ดังนั้นตัวเลขabc.de φ (โดยที่aถึงeเป็นจำนวนเต็ม) จะแทน

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

โปรดทราบว่าโดยหลักการแล้วตัวเลขใด ๆ อาจเป็นลบได้ (แม้ว่าเราจะไม่คุ้นเคย) - เราจะแสดงตัวเลขลบด้วยการนำ~หน้า สำหรับจุดประสงค์ของคำถามนี้เรา จำกัด ตัวเองเป็นตัวเลขจาก~9ถึง9ดังนั้นเราจึงสามารถเขียนตัวเลขเป็นหนึ่งสตริงได้อย่างไม่น่าสงสัย ดังนั้น

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

~290.~43จะเขียนเป็น เราเรียกหมายเลขนี้ว่าเลขฐานสอง

ตัวเลข phinary สามารถแสดงในรูปแบบมาตรฐานได้เสมอซึ่งหมายความว่าการแสดงนั้นใช้เฉพาะตัวเลข1และ0โดยไม่มีที่11ใดก็ได้และมีเครื่องหมายลบที่เป็นตัวเลือกเพื่อระบุว่าจำนวนทั้งหมดเป็นลบ (น่าสนใจว่าจำนวนเต็มทุกตัวมีการแทน จำกัด เฉพาะในรูปแบบมาตรฐาน)

การแสดงที่ไม่ได้อยู่ในรูปแบบมาตรฐานสามารถแปลงเป็นรูปแบบมาตรฐานได้เสมอโดยใช้การสังเกตต่อไปนี้:

  1. 011 φ = 100 φ (เพราะφ 2 = φ + 1)
  2. 0200 φ = 1001 φ (เพราะφ 2 + 1 / φ = 2φ)
  3. 0 ~ 10 φ = ~ 101 φ (เพราะφ - 1 / φ = 1)

นอกจากนี้:

  1. หากตัวเลขที่สำคัญที่สุดคือ~1(โดยส่วนที่เหลือเป็นรูปแบบมาตรฐาน) จำนวนนั้นเป็นค่าลบและเราสามารถแปลงเป็นรูปแบบมาตรฐานโดยการแลกเปลี่ยนทั้งหมด1และ~1เตรียมเครื่องหมายลบและใช้กฎทั้งสามข้างต้นอีกครั้งจนกว่าเราจะ ขอรับแบบฟอร์มมาตรฐาน

นี่คือตัวอย่างของการทำให้เป็นมาตรฐานดังกล่าว(ฉันกำลังใช้ช่องว่างเพิ่มเติมสำหรับตัวเลขบวกเพื่อจัดตำแหน่งแต่ละตำแหน่งให้ตรงกัน): 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)

ยอมให้-0.0101φ

สำหรับการอ่านเพิ่มเติม Wikipedia มีบทความที่ให้ข้อมูลมากในหัวข้อ

ความท้าทาย

ดังนั้นหรือมิฉะนั้นเขียนโปรแกรมหรือฟังก์ชั่นซึ่งได้รับสตริงที่เป็นตัวแทนของจำนวน phinary (ตามที่อธิบายไว้ข้างต้น) ส่งออกรูปแบบมาตรฐานของมันโดยไม่ต้องนำหน้าหรือต่อท้ายศูนย์ อินพุตไม่จำเป็นต้องมีจุด phinary แต่จะมีตัวเลขที่เหลืออยู่เสมอ (ดังนั้นไม่ใช่.123) เอาต์พุตจะต้องมีจุด phinary เสมอและอย่างน้อยหนึ่งหลักทางด้านซ้ายของมัน

คุณสามารถรับอินพุตผ่าน STDIN, ARGV หรืออาร์กิวเมนต์ของฟังก์ชันและส่งคืนผลลัพธ์หรือพิมพ์ไปที่ STDOUT

คุณอาจใช้อัลกอริทึมที่แตกต่างจากขั้นตอนข้างต้นตราบใดที่มันอยู่ในหลักการที่ถูกต้องและแน่นอนสำหรับอินพุต (ถูกต้อง) โดยพลการ - นั่นคือข้อ จำกัด เพียงอย่างเดียวที่อาจทำให้การใช้งานของคุณแตกหักนั้นควรเป็นข้อ จำกัด ทางเทคนิคเช่นขนาดของเครื่อง ชนิดข้อมูลหรือ RAM ที่มีอยู่ ตัวอย่างเช่นการประเมินอินพุตเป็นหมายเลข floating-point และไม่อนุญาตการเลือกตัวเลขอย่างโลภเนื่องจากเป็นหนึ่งสามารถค้นหาอินพุตที่ความไม่ถูกต้องของ floating-point นำไปสู่ผลลัพธ์ที่ไม่ถูกต้อง

นี่คือรหัสกอล์ฟคำตอบที่สั้นที่สุด (เป็นไบต์) ชนะ

กรณีทดสอบ

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

ตอนนี้ฉันต้องการใช้ตัวเลขลบในตัวเลขของฉัน! 1 ~ 3 * 6 == 5 ~ 8
แอรอน

คำตอบ:


6

Javascript (ES6) - 446 418 422 420 ไบต์

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,'')}

ขยาย:

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, '' );
}

รหัสสร้างฟังก์ชันFที่ดำเนินการแปลงที่ระบุ

มันเป็นปัญหาที่ยากสำหรับการเล่นกอล์ฟ กรณีขอบจำนวนมากคืบคลานขึ้นเพื่อป้องกันการทำให้โค้ดง่ายขึ้น โดยเฉพาะอย่างยิ่งการจัดการกับเนกาทีฟเป็นความเจ็บปวดทั้งในแง่ของการแยกวิเคราะห์และในแง่ของการจัดการเชิงตรรกะ

ฉันควรทราบว่ารหัสจะจัดการกับ "ช่วงที่เหมาะสม" ของอินพุตเท่านั้น หากต้องการขยายโดเมนของฟังก์ชันโดยไม่ จำกัด จำนวนของเลขศูนย์zสามารถเพิ่มขึ้นได้และค่าคงที่ของwhile( c++ < 99 )ลูปจะเพิ่มขึ้น ช่วงที่รองรับในปัจจุบันนั้นมีค่าใช้จ่ายมากเกินไปสำหรับกรณีทดสอบที่ให้มา

ตัวอย่างผลลัพธ์

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.ไม่สวย แต่คำตอบคือคงถูกต้อง ฉันสามารถแก้ไขได้ถ้าจำเป็น


@ MartinBüttner: ทำได้ แต่มันคงเป็นเรื่องยาก มัน จำกัด จำนวน "pass" เหนืออินพุตแบบเต็มและแต่ละ pass ประกอบด้วยการดำเนินการหลายอย่าง รู้สึกลำไส้ของฉันคือว่าจำนวนของอ้อมที่จำเป็นในการทำให้ปกติใด ๆnการป้อนข้อมูล -digit จะเป็นที่ไหนสักแห่งระหว่างและn n log(n)ไม่ว่าในกรณีใดจำนวนการผ่านสามารถเพิ่มได้ 10 เท่าสำหรับการเพิ่มตัวละครทุกตัว จำนวนศูนย์ในzค่าคงที่ยังเป็นปัญหาที่น่าสนใจ ฉันสงสัยว่า 9 นั้นมากไปสำหรับการป้อนข้อมูลใด ๆ ที่เป็นไปได้
COTO

@ MartinBüttner: ขอบคุณ ฉันลบการหลบหนีออกจากคลาสตัวละคร สำหรับ$0Javascript นั้นไม่รองรับ หรืออย่างน้อย Firefox ก็ไม่ได้ : P
COTO

โอเคฉันคิดว่าคุณไม่จำเป็นต้องมีเลขศูนย์นำหน้ามากกว่า 7 ตัวเป็นบัฟเฟอร์ แต่ฉันคิดว่าเลขศูนย์ต่อท้ายนั้นจะยากกว่าที่จะประมาณ สำหรับวงด้านนอกฉันไม่คิดว่าคุณจะต้องการมันถ้าคุณทำแบบนั้นสักครู่ในขณะที่ (หรือรวมเข้าไปในวงในสำหรับวงวน) และเพิ่งแตกออกเมื่อไม่พบการเปลี่ยนแปลงอีกต่อไป ฉันเดาสเป็คของฉันอาจจะชัดเจนขึ้นเล็กน้อยในแง่นั้น แต่โดย "ในหลักการที่ถูกต้องและแน่นอนสำหรับอินพุต (ถูกต้อง) โดยพลการ" ฉันหมายถึงว่าข้อ จำกัด ทางทฤษฎีเท่านั้นควรมีขนาดของประเภทข้อมูลในตัว / RAM ของคุณ
Martin Ender

1
@COTO เพื่อบันทึก 1 byte คุณสามารถลองย้ายส่วนแรกของfor( i = e = 0; i < n-3; i = j )by for(; i < n-3; i = j )และย้ายการประกาศไปด้านบนโดยN = t = n = 0;แทนที่ด้วยN = t = n = i = e = 0;
Ismael Miguel

1
@IsmaelMiguel: ไม่ได้จัดขึ้นอย่างต่อเนื่องที่ค่าของj i+1แจ้งให้ทราบล่วงหน้าในช่วงสามifบล็อกตั้งค่าใหม่เป็นj 0ดังนั้นที่จุดใด ๆ หลังจากที่ครั้งแรกบล็อกก็ไม่สามารถนำมาใช้เป็นพร็อกซี่สำหรับif i+1ตัวแปรiนั้นไม่สามารถอัปเดตได้จนกว่าจะสิ้นสุดลูป (ใช้คำสั่งที่สามในfor) เนื่องจากมีการใช้ค่าจนถึงจนถึงสิ้นสุดลูป แต่ต้องบอกว่าฉันอาจจะพลาดบางอย่าง หากคุณสามารถย่อรหัสทดสอบและยืนยันว่ายังใช้งานได้โปรดโพสต์สำเนาไปที่ pastebin.com และลิงค์ที่นี่ ฉันจะขยายเครดิตให้คุณในคำตอบ :)
COTO

2

Haskell, 336 ไบต์

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

นี่คืออัลกอริธึมโลภ แต่ด้วยการแทน[a,b]ตัวเลขa + ( a , b ∈ℤ) ที่แน่นอนเพื่อหลีกเลี่ยงข้อผิดพลาดจุดลอยตัว g[a,b]ทดสอบว่าa + <0. ตัวอย่างการใช้งาน:

*Main> f "9"
"10010.0101"
*Main> f "1~3.2~1"
"-0.0101"
*Main> f "0.~1021"
"0."
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.