与阶乘数系统相互转换


27

阶乘数系统,也称为factoradic,是一个混合基数的数字系统。阶乘决定数字的位数。

在此系统中,最右边的数字可以是0或1,第二个最右边的数字可以是0、1或2,依此类推。这意味着n数字乘数的最大值可以为(n + 1)!

例如,要将乘数转换24201为十进制,您可以这样做:

2 * 5! = 240
4 * 4! = 96
2 * 3! = 12
0 * 2! = 0
1 * 1! = 1
240 + 96 + 12 + 0 + 1 = 349

因此,阶乘数24201349base 10

要将十进制数字(349例如)转换为因数,您可以这样做:

取最大乘以小于该数。在这种情况下,它是1205!

349 / 5! = 2 r 109
109 / 4! = 4 r 13
13 / 3! = 2 r 1
1 / 2! = 0 r 1
1 / 1! = 1 r 0

因此,349基数10为因数24201

您面临的挑战是创建最短的程序或函数,以将输入数字转换为其他基数。

输入将是非负整数的字符串表示形式。阶乘数将以!(例如!24201)开头,而十进制数将不以任何形式开头。您可以假定最大输入将为10! - 1- 3628799以十进制和987654321以因数表示。这意味着字母将不会出现在分解输入/输出中。

该程序不需要!在乘数输出前加上a ,可以输出字符串或整数。输入可以采用任何合理的格式。


测试用例:

Input: 1234
Output: 141120

Input: 746
Output: 101010

Input: !54321
Output: 719

Input: !30311
Output: 381

Answers:


10

APL,39 37个字符

{A B←(9⍴10)(⌽1+⍳9)⌽⍨'!'∊⍵⋄A⊥B⊤⍎⍵~'!'}

例子:

      {A B←(9⍴10)(⌽1+⍳9)⌽⍨'!'∊⍵⋄A⊥B⊤⍎⍵~'!'}'1234'
141120
      {A B←(9⍴10)(⌽1+⍳9)⌽⍨'!'∊⍵⋄A⊥B⊤⍎⍵~'!'}'!54321'
719

1
我想你可以替换⍴⍵∩'!''!'∊⍵保存字符。
波动率

@Volatility是的,可以。我还找到了另一个。
霍华德

11
在脚本中包含“ pwn”一词的IMO非常值得额外使用。
ejrb

1
我同意ejrb。你能把这个分解一下吗?
泰特斯

1
替换~'!'∩⎕D以保存字符。
ADAM

9

蟒2.7(163 157 152)

i=raw_input()
exec("b='';a=362880;j=int(i);x=9;"+'b+=`j//a`;j%=a;a/=x;x-=1;'*9,"a=x=1;b=0;"+'b+=a*int(i[-x]);x+=1;a*=x;'*~-len(i))['!'in i]
print int(b)

更具可读性的版本:

i=raw_input()
if'!'in i:a=x=1;b=0;c='b+=a*int(i[-x]);x+=1;a*=x;'*~-len(i)
else:b='';a=362880;j=int(i);x=9;c='b+=`j//a`;j%=a;a/=x;x-=1;'*9
exec c;print int(b)

分解:

Factoradic -> Decimal, when i is in the form !(number)
a=1   #Factorial value (multiplied every iteration)
x=1   #Index value
b=0   #Output
iterate ~-len(i) times:    #PSEUDOCODE! bitwisenot(a) = ~a = -a-1
    b+=a*int(i[-x])        #add the value of the xth last character in the factoradic #
    x+=1                   #Increment x
    a*=x                   #Set a to x!, (x-1)! * x = x!

Decimal -> Factoradic
b=''                       #Output
a=362880                   #Factorial value, set to 9! here
j=int(i)                   #Integer value of the input
x=9                        #Index value
iterate 9 times:           #PSEUDOCODE! This block is in an exec() loop
    b+=`j/a`               #Add floor(j/a) to b
    j%=a                   #Take out all multiples of a in j
    a/=x                   #Set a to (x-1)!, x! / x = (x-1)!
    x-=1                   #Decrement x

1
不错的解决方案。我想你可以替换'!'==i[0]使用'!'in i,并且可以使用a=x=1。另外,您不需要在exec语句两边加括号。
grc

1
您也可以替换(len(i)-1)~-len(i)
波动率

@波动率,grc:谢谢!我应该学习按位运算符:)
beary605

1
好的答案,我冒昧地尝试用if语句替换if语句,(a,b)['!'in i]并设法删除了6个字符。它不像以前那样可读... pastebin链接
ejrb

@erjb:谢谢你的建议!我使用了一个2元组,并将代码作为与exec函数内联的字符串,这节省了另外两个字符:)
beary605

8

GolfScript(48 44 43个字符)

.~\{1{):?\.?%\?/@}9*{*+}+9*}:^{:N,{^N=}?}if

这是一个独立的程序。阶乘=>小数转换非常慢,因为它使用十进制=>阶乘转换而不是直接基数转换进行搜索。

输入格式允许非常短的模式切换:.~复制输入字符串并对其求值,因此,如果输入只是一个数字,我们以例如"1234" 1234堆栈结尾,如果以!(逻辑非,任何非空开头)字符串是真实的),我们最终0 30311在堆栈上。然后,对于小数=>阶乘,堆栈底部的值是真实的;对于小数=>十进制,栈底的值是虚假的。


4

PHP <7.1 178 171 170 168 164 155 147 144 138 126 123字节

for($b=$j=1,$i=strlen($x=$argn);+$x?$b<=$x:--$i;$b*=++$j)$r+=$x[$i]*$b;if(+$x)for(;$j>1;$x%=$b)$r.=$x/($b/=$j--)|0;echo+$r;

作为管道运行-r在线测试

  • 无需扩展
  • 不需要功能:阶乘基被重用(循环中增加/减少)
  • 纯整数和字符串算术,甚至应该可以在php 3中工作(并且仍然可以在php 7中工作):
  • 十进制0返回空字符串,而不是0(其他两个PHP答案也都可以。)如果那是不可接受的,则在特殊情况下加+5。

松散:

// two loops in one: compute the decimal number from a factorial
// or find the first factorial larger than a decimal $x
// the latter inits $r with '0': $i=strlen -> $x[$i]=='' -> (int)$x[$i]==$x[$i]*$b==0
// $b is the current digit´s base; $j is the bases´ latest factor
for($b=$j=1,$i=strlen($x=$argn);+$x?$b<=$x:--$i;$b*=++$j)
    $r+=$x[$i]*$b;
// and now for dec->fact ...
if(+$x)
    for(;$j>1;$x%=$b)
        // both $b and $j are one step too far in the first iteration;
        // -> decrement must precede the actual loop body
        // -> can be merged into the digit calculation -> all braces golfed
        $r.=$x/($b/=$j--)|0;
        // now: go on with the remainder (see loop head)
echo+$r; // final type cast removes leading zeros (from the first loop)
    // and fixes the '0' result (no operations at all on that input!)

放弃打高尔夫球的想法:

  • $b<=$x-> $b<$x(-1)
    会破坏纯十进制阶乘(即那些导致阶乘数只有一个非零数字的阶乘)。JMPC的解决方案因此而受苦。HamZa的不是。
  • floor($x/$b)-> (int)($x/$b)
    可能会快一点,但是类型转换先于除法,所以我需要括号并且不增加字节。
    $x/$b|0绝招
  • 实际上-> dec的循环类似于dec-> fact中的阶乘查找。增量相同,主体无所谓,但遗憾的是预设不同,后期条件也不同。荡 本来可以打-21。
    是的,我找到了解决方案。打了很多高尔夫球,但砍掉了另一个-4(否:-9),关闭了所有的漏洞/漏洞。

还有潜力...还是我打高尔夫球了?


@JörgHülsermann感谢您的提示。
泰特斯(Titus)

1
+$r而不是$r|0保存一个字节。同为if($x|0)
约尔格Hülsermann

3

的JavaScript(ES 6)139 137 122 113 111

尝试了使用数组魔术的另一种方法;但是我最终达到了174172字节:

f=x=>{if('!'==x[0]){a=x.split``.reverse();i=b=1;r=0;a.pop();a.map(d=>{r+=d*b;b*=++i})}else{t=[];for(i=b=1;b<=x;b*=++i){t.unshift(b)}r='';t.map(b=>{r+=x/b|0;x%=b})}return r}

所以我只是拿了我的PHP代码并进行了翻译。可以删除所有$s和一些;,但是初始化vars的必要性吞噬了其中的一些好处。不过,设法将两个答案都打得更远。

打高尔夫球

f=x=>{for(r=0,b=j=1,i=x.length;x|0?b<=x:--i;b*=++j)r+=x[i]*b;if(x|0)for(r='';j>1;x%=b)r+=x/(b/=j--)|0;return r}
  • 第一个版本返回''代表十进制0; 加+2修复
  • 第二个版本需要字符串输入
  • 都在Firefox,Edge和Opera中进行了测试

不打高尔夫球

f=x=>
{
    for(r=0,b=j=1,i=x.length;x|0?b<=x:--i;b*=++j)
        r+=x[i]*b;
    if(x|0)
        for(r='';j>1;x%=b)
            r+=x/(b/=j--)|0;
    return r
}

测试套件

<table id=out border=1><tr><th>dec</th><th>result<th>expected</th><th>ok?</th></tr></table>
<script>
    addR=(r,s)=>{var d=document.createElement('td');d.appendChild(document.createTextNode(s));r.appendChild(d)}
    test=(x,e)=>{var y=f(x),r=document.createElement('tr');addR(r,x);addR(r,y);addR(r,e);addR(r,e==y?'Y':'N');document.getElementById('out').appendChild(r)}
    samples={'349':'24201','1234':'141120','746':'101010','719':'54321','381':'30311','24':'1000','0':'0'};
    for(d in samples){test(d,samples[d]);test('!'+samples[d],d)}
</script>

1
ES5没有箭头符号IIRC。如果你要使用ES6,然后.split('')=>.split``
扎卡里

@ZacharýGna我应该注意到我在Firefox或Opera中测试过的浏览器。那么ES 6?
泰特斯

是的,箭头符号是ES6。
扎卡里

1
哦,这让我措手不及,我认为最上面的代码块是您的解决方案!无论如何,我认为您不必说f=。此外,可以r+=(x/(b/=j--)|0)r+=x/(b/=j--)|0
扎卡里


1

GolfScript,69个字符

10,1>{1$*}*](.0=33={1>01/-1%0\{~@(@*@+}/\}{~\-1%{1$<},{1$1$/@@%}/}if;

照常接收来自STDIN的输入并打印结果。在线测试


1

Haskell,221个字符

高尔夫代码

m v@(a:b)|a=='!'=(sum.zipWith(*)g.map(read.(:[])).reverse) b|True=(fst.until((<0).fst.snd)(\(s,(i,b))->(s*10+b`quot`f i,(i-1,b`rem`f i))).(\n->(0,((1+).last.takeWhile((n>=).f)$[1..], n))).read) v;g=scanl1(*)[1..];f=(g!!)

用法

$ ghci factorial.hs
ghci> m "1234"
 141120
ghci> m "!54321"
 719

非高尔夫代码

parse v@(a:b) | a == '!' = to b
              | otherwise = from v

to = sum . zipWith (*) factorials . map (read . (:[])) . reverse

from = fst . until finished next . boostrap . read
    where finished = ((<0) . fst . snd)
          next (s,(i,r)) = (s * 10 + r `quot` factorial i, (i-1 ,r `rem` factorial i))
          bootstrap n = (0, (lastFact n, n))
          lastFact n = (1+) . last . takeWhile ((n>=) . factorial) $ [1..]

factorials = scanl1 (*) [1..]

factorial = (factorials!!)

迄今为止最易读的条目。Haskell FTW!
苏厄姆·乔杜里

1

数学213 177 175

f[]不管是输入还是输出,阶乘号都包装在中。

g@{n_,j_,r_}:=If[j==0,FromDigits@r,g@{q=QuotientRemainder[n,j!];q[[2]],j-1,Append[r,q[[1]]]}]
z@n_:=If[!IntegerQ@n, g[{n[[1]],9,{}}], f@Tr@(p=1;# (p++)!&/@Reverse@IntegerDigits@n)]

用法

z[24201]

f [349]

z[f[349]]

24201

阶乘转换为十进制数QuotientRemainder[n,j!]从左到右递归作用于阶乘数的数字,并j在每一步递减。 QuotientRemainder[349, 5!],例如返回{2, 109}等。

十进制转换为阶乘数。从右向左移动纯函数,# (p++)! &将每个数字乘以#适当的阶乘。


1

Python,128个字符

运行大约需要半小时,但是它很小:

A=[`x`for x in xrange(10**9)if all(x/10**d%10<d+2 for d in range(9))]
i=raw_input()
print A.index(i[1:])if'!'in i else A[int(i)]

它以数字顺序构建所有<= 9位数字阶乘数的列表,然后进行查找或索引转换。

如果你想测试,只需更换10**910**6和限制自己的6位数字可变参数。

从技术上讲,我可以使用range(10**9)代替来保存字符xrange(10**9)。不要在家尝试这个。


需要之间没有空格d+2for
扎卡里

1

PHP 231 214 204

最新答案

function g($x){return $x?$x*g($x-1):1;}function f($x,$e){if($x[0]=="!"){for($t=1;$t<$c=strlen($x);$t++){$e+=$x[$t]*g($c-$t);}}else{while(g(++$p)<=$x);while(--$p){$e.=floor($x/g($p));$x%=g($p);}}return$e;}

旧答案

 function f($n){if($n[0]=="!"){$n=str_split($n);$c=count($n);$f=$y=1;while($c-->1){$e+=($f*$n[$c]);$f*=++$y;}return$e;}else{for($i=$c=1;$i<$n;$i*=$c){$r[$c++]=$i;}foreach(array_reverse($r)as$t){$e.=floor($n/$t);$n=$n%$t;}return$e;}}

echo f('349')."\n"
    .f('!24201')."\n"
    .f('1234')."\n"
    .f('746')."\n"
    .f('!54321')."\n"
    .f('!30311');

输出量

24201
349
141120
101010
719
381

2
我为新答案计算212,而不是214。$ e不需要初始化(-6),foreach(range())可以将其替换为简单for循环(-9)。我喜欢这个主意。
泰特斯(Titus),2016年

2
纯析因的错误结果。24应该返回1000但返回400。修复:g(++$p)<$x-> g(++$p)<=$x(+1)
泰特斯

@Titus感谢您的两个答复!我更新了答案。感谢您帮助我改善您的答案,而您的答案似乎要好得多。
JPMC '16

1
1)我再一次比您少2:204,而不是206。您在字节数中是否包含Windows换行符?2)构造中的语法错误for,应该为;3)我还有另外7个更改,在该代码上节省了20个字节。要他们?
泰特斯

2
好吧,实际上只有5个更改,但是其中一个包含三个部分。a)f()(-3)的过时第二个参数b)函数g(-1)中的过时空白c)true分支中的过大括号(-4)d)交换true和false分支,将if条件求逆,然后使用我的性感类型强制转换为int(-6)这不会影响十进制0的结果!e)for可以很好地重写其余构造while(++$t<$c=strlen($x)):在主体前增加-> $ t不需要初始化(-6)
Titus

1

JELLY,5个字节

Æ!ŒṘ€

说明

Æ!ŒṘ€
Æ!     -Convert to factoriadic (list form)
  ŒṘ€  -Construct string

*果冻比问题年龄小,因此我的回答没有竞争力。


1
欢迎来到PPCG!我认为果冻比这项挑战还年轻,因此您应该将答案标记为非竞争。
Laikoni

哦,我没有意识到那是规则。会做。
DaggerOfMesogrecia

对这个挑战的答案不是要双向工作吗?这似乎只能以一种方式起作用。您可能要修复该问题。(另外请注意,如果您要在Jelly中在整数和字符串之间进行转换,通常使用V和的组合是最

1

果冻,15字节

ḊV€;0Æ¡µÆ!ṖḌƊ¬?

在线尝试!

怎么运行的

ḊV€;0Æ¡µÆ!ṖḌƊ¬?     Main link (monad). Input: integer or string
             ¬?  *) If the given input is a string, run 1); otherwise run 2)

ḊV€;0Æ¡  1) Factorial base -> integer
ḊV€         Remove "!" and map each char to number
   ;0       Append zero (this is needed to run the built-in correctly)
     Æ¡     Built-in conversion

Æ!ṖḌ  2) Integer -> factorial base
Æ!       Built-in conversion
  ṖḌ     Remove a zero at the end, and convert to decimal

为什么*)起作用

¬在元素方面是逻辑非。给定单个整数时,它将变为单个零,这是错误的。但是,当给定字符串时,每个元素(字符)都将变为零,并且整个结果是一个零数组,这是正确的。

零为整数是一种特殊情况。它通过“阶乘->整数”路径,但仍然给出零,这是正确的。

没有内置阶乘基数,25个字节

⁵R!µ³%Ḋ:ṖUḌ
⁵R!ḋḊUV€ƊµÇ¬?

在线尝试!

怎么运行的

⁵R!µ³%Ḋ:ṖUḌ  Aux. link (monad). Integer -> factorial base
⁵R!µ         Set (1..10)! as left argument
    ³%Ḋ:Ṗ    Compute each digit: (input % (2..10)!) // (1..9)!
         UḌ  Reverse and convert the digit array to decimal

⁵R!ḋḊUV€ƊµÇ¬?  Main link (monad).
         怪?  If the input is a string, apply the left chain;
               otherwise, apply the aux. link above
⁵R!            (1..10)!
   ḋ           Dot product with...
    ḊUV€Ɗ      Remove "!", reverse, map each character to digit

0

K,102

"I"$,/$*:'|:'{{(x-y*g),g:_(x:*x)%y:*/1+!y}\[x,0n;|1+!{$[(x>(*/1+!y))&x<*/1+!y+1;y;.z.s[x;1+y]]}[x;0]]}

绝对可以改善。

k)"I"$,/$*:'|:'{{,[;g]x-y*g:_(x:*x)%y:*/1+!y}\[(x;0n);|1+!{{$[(x>(*/1+!y))&x<*/1+!y+1;y;.z.s[x;1+y]]}[x;0]}x]} 349
24201
k)"I"$,/$*:'|:'{{,[;g]x-y*g:_(x:*x)%y:*/1+!y}\[(x;0n);|1+!{{$[(x>(*/1+!y))&x<*/1+!y+1;y;.z.s[x;1+y]]}[x;0]}x]} 746
101010
k)"I"$,/$*:'|:'{{,[;g]x-y*g:_(x:*x)%y:*/1+!y}\[(x;0n);|1+!{{$[(x>(*/1+!y))&x<*/1+!y+1;y;.z.s[x;1+y]]}[x;0]}x]} 1234
141120

0

D(159个字符)

int x(string n){import std.conv;int r,i=9,f=9*'鶀',d;if(n[0]<48){while(r.text.x<n[1..$].to!int)r++;}else{d=n.to!int;while(i){r=r*10+d/f;d%=f;f/=i--;}}return r;}

取消高尔夫并且带有程序入口点

所有命令行参数均打印为<original> -> <converted>。实际上在中仅实现了十进制到因子x。另一种方法是仅x使用所有十进制数字(0 .. *)进行调用,直到结果等于输入为止。最大的输入(!987654321)大约需要3秒。

可执行的在线版本:http//dpaste.dzfl.pl/46e425f9

void main(string[] args) {
    import std.stdio;
    foreach (arg; args[1 .. $]) {
        writefln("%s -> %s", arg, x(arg));
    }
}

int x(string n) {
    import std.conv;
    int r, i=9, f=9*'鶀', d;  // 鶀's Unicode index equals 8*7*6*5*4*3*2*1

    // If the first character value is less than 48 ('0') it should be a '!'.
    if (n[0] < 48) {
        // Call x with different input (0..*) until it matches our n.
        // r.text.x is rewritten as x(text(r)).
        while (r.text.x < n[1..$].to!int) r++;
    } else {
        d = n.to!int;
        // Try d / 9!, d / 8!, etc. just as in the problem description.
        while (i) {
            r = r*10 + d/f;
            d %= f;
            f /= i--;
        }
    }
    return r;
}

我认为可能可以更改string nchar[]n保存一个字节(我知道我来晚了)。
扎卡里

另外,我认为if(n[0]<48){while(r.text.x<n[1..$].to!int)r++;}可以if(n[0]<48)while(r.text.x<n[1..$].to!int)r++;节省两个字节。
扎卡里

0

VBA 225

感谢Titus的帮助!还在寻找更多的高尔夫。

Sub a(b)
Set w=WorksheetFunction
e=Len(b)
If IsNumeric(b) Then
i=0
For d=0To 8
h=w.Fact(9-d)
g=b Mod h
If g<b+i Then
i=1
f=f &Int(b/h)
b=g
End If
Next
Else
For d=2To e
f=f+w.Fact(e-d-1)*Mid(b,d,1)
Next
End If
MsgBox f
End Sub

我不知道VBA,但是有没有一种方法可以检查b数字值而不是比较第一个字符?
泰特斯(Titus)

@Titus有一个数字检查,这里的等效项是:If Not IsNumeric(b) Then,但这需要更多字符。现在,我没有再检查所有代码。IsNumeric总体而言,可能会有更好的方法。-更正,这里有一点改进。谢谢!
加菲2016年

我发现了另外四个字节:For d=9To 1Step-1Fact(d)- > For d=0To 8Fact(9-d)与另外两个如果你For d=2To eFact(e-d+1)*Mid(b,d,1)
提图斯

转换为Int的类型可以用其他方式编写吗?
泰特斯(Titus)

@Titus看着你,绕着我转圈。:)我现在要进行调整...至于Int(),我认为没有更简单(更小)的方法了。
加菲

0

PHP,124字节

for($f=1;("$">$a=$argn)&&~$c=strrev($a)[$n];)$r+=$c*$f*=++$n;for(;$a>=$f*=++$i;);for(;~-$i;$a%=$f)$r.=0|$a/$f/=$i--;echo+$r;

在线尝试!

扩展的

for($f=1;("$">$a=$argn)&&~$c=strrev($a)[$n];) # runs in case of "!" at the beginning
  $r+=$c*$f*=++$n; #reverse string multiply with the next factorial "!"*$f=0
for(;$a>=$f*=++$i;); # runs not in case of "!" at the beginning string comparing. search the factorial that is higher as input value
for(;~-$i;$a%=$f) # runs only when the second loop had runs
  $r.=0|$a/$f/=$i--; # concat the value of the division with the highest factorial used
echo+$r; # Output result

0

Perl 6,150个字节

{/^\!/??([+] [Z*] .comb.skip.reverse,[\*] 1..*)!!(reduce
->\a,\b{a[0]~a[1] div b,a[1]%b},("",+$_),|(first
*[*-1]>$_,[\,] [\*] 1..*).reverse[1..*])[0]}

0

APL(NARS),36个字符,72个字节

{⍵⊆⎕D:10⊥(9..2)⊤⍎⍵⋄t+.×⌽!⍳≢t←⍎¨,1↓⍵}

似乎10⊥(9..2)⊤比递归函数更好,这要归功于Howard的另一种APL解决方案,它表明...(即使我不理解100%)。输入不带“!”的数字 <10 !. 测试:

  u←{⍵⊆⎕D:10⊥(9..2)⊤⍎⍵⋄t+.×⌽!⍳≢t←⍎¨,1↓⍵}    
  u¨'1234' '746' '!54321' '!30311' '!24201'    
141120 101010 719 381 349 
  u '0'
0
  u '!0'
0
  u '9'
111
  u '!111'
9
  u '!9'
9
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.