标准化电话号码


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*φ^2 + b*φ^1 + c*φ^0 + d*φ^-1 + e*φ^-2

请注意,原则上任何数字都可以为负(尽管我们不习惯于此)-我们将以负号表示一个负数~。对于这个问题,我们限制自己,从数字的目的~99的,所以我们可以毫不含糊地写(在与之间的波浪线),一个数字作为一个字符串。所以

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

将被写为~290.~43。我们称这样的数字为非凡数字

索号始终可以以标准形式表示,这意味着该表示仅使用数字10,而不包含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提供了有关该主题的非常有用的文章

挑战

因此,或者以其他方式,编写一个程序或函数,给定一个表示一个整数的字符串(如上所述),然后输出其标准格式而没有前导或尾随零的程序或函数。输入不一定包含枢纽点,但将始终包含其左边的数字(所以没有.123)。输出必须始终包含麻点,并在其左侧至少包含一位数字。

您可以通过STDIN,ARGV或函数参数获取输入,然后返回结果或将其打印到STDOUT。

您可以使用与上述过程不同的算法,只要它在原则上对任意(有效)输入都是正确和准确的-也就是说,可能破坏您的实现的唯一限制应该是技术限制,例如内置大小数据类型或可用的RAM。例如,不允许将输入评估为浮点数,然后贪婪地挑选数字,因为可能会发现输入的浮点错误会导致错误的结果。

这是代码高尔夫球,最短的答案(以字节为单位)获胜。

测试用例

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 ==
亚伦

Answers:


6

使用Javascript(ES6) - 446个 418 422 420字节

缩小:

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:可以,但是很难。它限制了整个输入上的“通过”次数,并且每个通过都包含多个操作。我的直觉是,标准化任意数字n输入所需的通过次数将在n和之间n log(n)。在任何情况下,每增加一个字符,通行次数都可以提高10倍。z常数中的零数目也是一个有趣的问题。我怀疑9对于任何可能的输入来说都是多余的。
COTO 2014年

@MartinBüttner:谢谢。我在角色类中删除了转义符。至于$0,Javascript不支持。或者至少Firefox没有。:P
COTO

好的,我认为您永远不需要超过7个前导零作为缓冲,但是我认为尾随零将很难估计。至于外部循环,如果您只是创建一个while循环(或将其集成到内部for循环中),并且在找不到更多更改时就中断,那么我什至不需要您。我猜想我的规格在这方面可能会更清晰一些,但是“原则上对任意(有效)输入是正确和准确的”,我的意思是,唯一的理论限制应该是内置数据类型/ RAM的大小。
Martin Ender 2014年

1
@COTO要保存1个字节,您可以尝试将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]数字的一个 + 一个b ∈ℤ),以避免浮点错误。g[a,b]测试a + <0。用法示例:

*Main> f "9"
"10010.0101"
*Main> f "1~3.2~1"
"-0.0101"
*Main> f "0.~1021"
"0."
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.