用最少的火柴进行数学


15

元背景

这是关于Puzzling的一个问题,即时反应是“嗯,有人会用计算机解决这个问题”。对于要解决这个问题的程序到底有多复杂,存在争议。好吧,“这个程序必须有多复杂”几乎就是的定义,所以也许PPCG可以解决这个问题?

背景

火柴方程基本上是一个正常的数学方程,但是其中数字和运营商通过将火柴到表物理构造。(这里的火柴棒的主要相关特征是它们相当坚固并且具有恒定的长度;有时人们会改用其他物体,例如棉签。)

对于这个挑战,我们不需要为火柴的排列方式定义特定的规则(就像链接的挑战一样)。相反,我们只关心表示一个计算为给定数字的表达式所需的火柴棍数量。

任务

这是您可以使用的数字字母和数学运算符,每个运算符的成本都包含在火柴棒中:

  • 0,花费6条火柴
  • 1,花费2个火柴棍
  • 2,花费5条火柴
  • 3,花费5条火柴
  • 4,花费4个火柴棍
  • 5,花费5条火柴
  • 6,花费6条火柴
  • 7,花费3个火柴棍
  • 8,花费7根火柴
  • 9,花费6条火柴
  • +,花费2个火柴棍
  • -,花费1个火柴棍
  • ×,花费2个火柴棍

(为了避免使用非ASCII字符,可以×根据需要*在程序的输出中表示为。在大多数编码中,×字节占用的字节数大于*,因此我想大多数程序都希望利用此余地)

您需要编写一个程序,该程序将非负整数作为输入(通过任何合理的方法),并生成一个计算结果为该整数作为输出的表达式(同样通过任何合理的方法)。此外,该表达式必须是平凡的:它必须包含至少一个运营商+-×。最后,就所有火柴棍成本而言,您输出的表达式在所有符合规范的输出中必须是最便宜的(或与最便宜的捆绑)。

澄清说明

  • 您可以通过在一行中输出多个数字来形成多个数字(例如,11-1要产生的有效输出10)。完全准确地说,所得数字将以十进制表示。这种级联不是对中间结果起作用的操作;仅针对原始表达式中出现的文字数字。
  • 为了这个挑战的目的。+-×是中缀运算符;他们需要左右左右有一个论点。不允许在前缀位置(例如+5或)中使用它们-8
  • 您没有可用的括号(或任何其他控制优先级的方法)。该表达式根据典型的默认优先级规则进行求值(首先进行乘法运算,然后从左至右对加法和减法求值)。
  • 除上面列出的数学运算符或常量外,您无权访问任何数学运算符或常量;Puzzling经常接受“横向思考”解决方案,但是要求计算机自己提出这些解决方案是没有意义的,在PPCG上,我们希望客观地找到解决方案是否正确。
  • 通常的整数溢出规则适用:您的解决方案必须能够在语言的假设(或实际)版本中使用任意大整数,默认情况下所有整数都是无界的,但是如果您的程序由于实现而在实践中失败不支持那么大的整数,这不会使解决方案无效。
  • 如果您多次使用相同的数字或运算符,则每次使用时都要支付其火柴成本(因为,显然,您无法在桌子上的两个不同位置重复使用相同的物理火柴)。
  • 没有时间限制;蛮力解决方案是可以接受的。(尽管您拥有比暴力破解还要快的解决方案,但即使更长,也可以随意发布;查看其他方法的比较总是很有趣的。)
  • 尽管从不需要编写代码说明,但这可能是个好主意。解决方案通常很难阅读(尤其是对于不熟悉其编写语言的人),并且除非您了解其工作原理,否则很难评估(并对其投票)。

胜利条件

作为挑战,认为字节数更少的答案更好。但是,像往常一样,即使它们比某些其他语言更冗长,也可以用不同的方法或特定的语言发布答案。高尔夫的目标实际上是看您可以优化某个特定程序的程度,以这种方式进行操作可以为我们提供许多潜在的程序来优化。因此,如果有人使用完全不同的方法或完全不同的语言提交解决方案并获得更短的答案,不要气disc。很可能是您的答案得到了更好的优化,并且显示了更多的技巧,PPCG的投票者经常对此表示赞赏。


哎呀,我们需要处理的最高人数是多少?我当前的尝试不会超出……可能在TIO上达到20。
Magic Octopus Urn

@carusocomputing:理论上很高,但是如果您在实践中不能在合理的时间内超过20,那完全可以接受。

4
你有测试用例吗?
路加福音

我真的希望这是一个单一的行动,可以在多个比赛中进行。乘法是一个除数问题,但是加法和减法的确使事情复杂化。我有一个可行的解决方案,但不能用于加法和减法;使其完美工作将是乏味的。
魔术八达通Ur

@carusocomputing:那么,您可能对此挑战感兴趣。我怀疑仅乘法的挑战会显着不同,并且需要相当不同的解决方法才能获得良好的成绩。

Answers:


1

得益于math_junkie,Python2、1×9×8×××××182字节

def f(n,c=dict(zip('0123456789+-*',map(int,'6255456376212'))),e=[(0,'')]):
 while 1:
    v=(m,s)=min(e);e.remove(v)
    try:
     if eval(s)==n:return s
    except:0
    e+=[(m+c[x],s+x)for x in c]

此算法无法排除和的前缀版本,但是它们的前缀对应版本会差于或等于前缀,+并且-在搜索后出现。因为它e可变地使用关键字参数,所以如果每个会话多次调用它将产生无效的结果。要解决此问题,请使用f(n, e=[(0,'')])而不是f(n)。请注意,四个空格的缩进表示制表符,因此仅适用于Python 2。

我也有一个经过优化的版本,即使数量很大也可以快速运行:

from heapq import heappop, heappush

def f(n):
    digits = list('0123456789')
    ops =['+','-','*','']
    costs = dict(zip(digits + ops, [6,2,5,5,4,5,6,3,7,6,2,1,2,0]))
    expressions = [(costs[d], abs(n - int(d)), int(d), d) for d in digits[1:]]
    seen = set()
    while 1:
        cost, d, k, expression = heappop(expressions)
        if d == 0:
            return expression
        for op in ops:
            if op in '+-' and k in seen:
                continue
            for digit in digits:
                if op and digit == '0':
                    continue
                expression1 = expression + op + digit
                k1 = eval(expression1)
                d1 = abs(n - k1)
                if d1 == 0:
                    return expression1
                heappush(expressions, (cost+costs[op]+costs[digit], d1, k1, expression1))
        seen.add(k)

建议的一些次要高尔夫运动:TIO(182字节)
数学迷

1

PHP,241字节

在线版本

function m($i){for(;$s<strlen($i);)$e+="6255456376"[$i[$s++]];return$e;}foreach($r=range(0,2*$a=$argv[1])as$v)foreach($r as$w)$x[$v+$w]["$v+$w"]=$x[$v*$w]["$v*$w"]=1+$x[$v-$w]["$v-$w"]=m("$v")+m("$w")+1;echo array_search(min($x[$a]),$x[$a]);

分解

function m($i){
    for(;$s<strlen($i);)$e+="6255456376"[$i[$s++]];return$e; #return the count of the matchstick for an integer
}

foreach($r=range(0,2*$a=$argv[1])as$v) # limit to an input to 300 in the online version
foreach($r as$w)
       $x[$v+$w]["$v+$w"]=  #fill the 2D array in the form [result][expression] = count matchsticks
       $x[$v*$w]["$v*$w"]=
       1+$x[$v-$w]["$v-$w"]=
       m("$v")+m("$w")+1;
echo $k=array_search(min($x[$a]),$x[$a]); # Output expression with a minium of matchsticks
echo"\t".$x[$a][$k]; #optional Output of the count of the matchsticks

性能更好的方式

function m($i){
for(;$s<strlen($i);)
$e+="6255456376"[$i[$s++]];return$e;} #return the count of the matchstick for an integer
foreach($r=range(0,2*$a=$argv[1])as$v)
foreach($r as$w){$c=m("$v")+m("$w")+1;
if($a==$v+$w)$x["$v+$w"]=1+$c; # fill array if value equal input
if($a==$v*$w)$x["$v*$w"]=1+$c;
if($a==$v-$w)$x["$v-$w"]=$c;}
echo $k=array_search(min($x),$x); # Output expression with a minium of matchsticks
    echo"\t".$x[$k]; #optional Output of the count of the matchsticks

支持负整数

带负整数的版本

function m($i){
    $e=$i<0?1:0; # raise count for negative integers
    for($s=0;$s<strlen($i);)$e+=[6,2,5,5,4,5,6,3,7,6][$i[$s++]];return$e; #return the count of the matchstick for an integer
}
$q=sqrt(abs($argv[1]));
$l=max(177,$q);
$j=range(-$l,$l); # for second loop for better performance
foreach($r=range(min(0,($a=$argv[1])-177),177+$a)as$v) 
foreach($j as$w){$c=m("$v")+m("$w")+1;  
    if($a==$v+$w)$x["$v+$w"]=1+$c; # fill array if value equal input
    if($a==$v*$w)$x["$v*$w"]=1+$c;
    if($a==$v-$w)$x["$v-$w"]=$c;
    if($a==$w-$v)$x["$w-$v"]=$c; # added for integers <0
}
echo $k=array_search(min($x),$x); # Output expression with a minium of matchsticks
echo"\t".$x[$k]; #optional Output of the count of the matchsticks

噢,这也适用于负数!
魔术八达通

@carusocomputing可能不是因为火柴棍少而导致的解决方案存在,因为负数只能通过减法相加。在这种情况下,你应该检查绝对值也并添加一个
约尔格Hülsermann

我不认为文字333在这里是可以接受的,尽管您可以通过使其具有某些输入功能来对其进行修复。(该程序运行速度可能会慢得多,因此您可以保留硬编码版本进行测试。)

1
@做333 ais523被替换为2 *输入
约尔格Hülsermann

1
您可以索引字符串:$e+="6255456376"[$i[$s++]];
manatwork'Mar
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.