给定字符串+=-
,其中至少有一个=
,在所有符号之间插入正整数,并在开始和结尾插入正整数,从而满足数学方程。
例如,给定输入
+-=-=
你需要像这样插入正整数A到F
A+B-C=D-E=F
所以方程都是满足的,即A + B - C
和D - E
和F
都是相同的数。
有许多可能的方法来做到这一点,因为,只要方程计算出来,任何一组正整数都可以使用。这里的每一行都可能是输入+-=-=
的有效输出:
2+3-4=6-5=1
1+1-1=2-1=1
4+2-4=4-2=2
100+1-10=182-91=91
89+231-77=1024-781=243
注意,表达式的值不需要像插入的数字那样是正整数。例如,给定输入-=-
,输出1-10=8-17
(evals to -9)和10-1=17-8
(evals to 9)都同样有效。当然,对于一些输入,如=
,不可能有负数作为表达式,因为只能插入像5=5
这样的正数。
还请注意,零不是正整数。
您可以将数字作为列表输出,而不是将它们直接插入到字符串中。如果要输出字符串,则可能会有分隔符号和数字的空格。因此,对于输入+-=-=
,输出
2, 3, 4, 6, 5, 1
或
2 + 3 - 4 = 6 - 5 = 1
等于输出
2+3-4=6-5=1
Input | One Possible Output
= | 1=1
== | 2=2=2
+= | 1+3=4
=+ | 2=1+1
-= | 30-10=20
=- | 1=2-1
=-= | 3=7-4=3
=+= | 2=1+1=2
=== | 100=100=100=100
+=- | 3+2=7-2
-=+ | 7-2=3+2
+=+ | 3+3=3+3
-=- | 1-10=8-17
--= | 60-1-1=58
++= | 60+1+1=62
-+= | 60-9+1=52
+-= | 60+9-1=68
+-=-= | 2+3-4=6-5=1
--=-- | 2-1-1=2-1-1
==-== | 47=47=50-3=47=47
=++=+-=-+=--= | 3=1+1+1=3+1-1=1-1+3=5-1-1=3
+--++-=-+-+- | 35+10-16-29+20+107-1000=5-4+3-2+1-876
====== | 8=8=8=8=8=8=8
发布于 2017-03-09 19:50:33
发布于 2017-03-10 03:46:51
x(L,R,V)-->{E#>0},({L=[E|R],E=V};x(L,[E|R],X),("+",{V#=X+E};"-",{V#=X-E})).
q(L,R,V)-->x(L,T,V),({T=R};"=",q(T,R,V)).
s(Q,L):-q(L,[],_,Q,[]),fd_labeling(L).
我们有很多方程要解决,为什么不使用一个实际的方程求解器呢?
x
基本上是形式+-+
方程的方程评估器;除了方程本身之外,它还有两个附加参数(包含方程值的差分列表L,R
和方程计算的值V
)。在Prolog中,它可以任意循环使用(例如,您可以指定V
和获取L,R
,指定L,R
和获取V
,指定两者并验证值是否正确,或者指定在任何情况下都不会对V
和L,R
设置适当的约束)。L,R
的“当前元素”名为E
,我们还包括一个断言,即E
大于0(因为这个问题需要使用正数)。这个函数比我想要的要详细一些,例如,我必须编写两次[E|R]
模式匹配/不匹配,因为列表是右关联的,但是加和减是左关联的。可悲的是,我们需要使用一个实际的列表,而不是发明出我们自己的左侧关联列表类型的反单元格,fd_labeling
才能工作。
q
类似于x
,但也包括=
。它基本上只是调用x
,并递归地调用它自己。顺便说一句,它非常清楚地演示了差异列表是如何工作的,它表明您可以将两个不同列表L,T
和T,R
连接到一个单独的差异列表L,R
中。基本思想是,差异列表是一个部分函数,它接受一个参数R
,并返回一个值L
,该值是R
,该列表本身就在其前面。因此,通过识别一个不同列表的参数和另一个不同列表的返回值,我们可以组合函数,从而将列表连接起来。
最后,s
是一个包装函数,它实际上解决了问题中的任务,它使用参数调用q
。通过提供[]
作为参数,将差分列表转换为正则列表,并使用fd_labeling
找到我们所建立的方程的解。(默认情况下,如果没有理由将值设置为其他值,则它似乎喜欢将值设置为1。但是,它是可以配置的;例如,value_method(random)
给出的“有趣”解决方案比到处放置1s更有趣,而且速度仍然很快。)
如所写的程序:
| ?- s("=++=+-=-+=--=", V).
V = [3,1,1,1,3,1,1,3,1,1,5,1,1,3] ?
如果我使程序添加value_method(random)
的时间稍微长一点,结果会有所不同,但如下所示:
| ?- s("=++=+-=-+=--=", V).
V = [68,6,12,50,85,114,131,45,3,26,71,1,2,68] ?
在这两种情况下,输出末尾的?
意味着可能有一个以上的解决方案。(当然,在这种情况下,我们知道有不止一个解决方案!)
发布于 2017-03-09 20:01:14
Join@@(Prepend[#^2,1-Min[Tr/@c]+Tr@#]&/@(c=Characters@StringSplit["0"<>#<>"0","="]/."+"->-1/."-"->1/."0"->Nothing))&
以字符串作为输入并返回正整数列表的纯函数。基本策略:我们只添加1,减去1,然后在每个表达式中选择初始值,使一切都相等。
c=Characters@StringSplit[#,"="]/."+"->-1/."-"->1
将在每个等号处拆分输入字符串,然后将每个+
替换为-1
,将每个-
替换为1
。然而,如果在开头或结尾有一个等号,那么它就会被忽略。因此,我们人为地在每一端添加一个新字符("0"<>#<>"0"
),并使其在字符串拆分完成(/."0"->Nothing
)之后消失。
每个子列表的总和现在等于一个整数,我们可以把它放在+
、S和-
s前面,使每个表达式相等。1-Min[Tr/@c]
是最小的整数,我们可以把它加到每个总数中,使它们都是正数。因此,Prepend[#^2,1-Min[Tr/@c]+Tr@#]&
接受每个子列表( ^2
将它们的所有条目转换为1
),并以最小的补偿整数作为其总移位的前面。生成的列表将Join
编辑在一起以生成输出。
https://codegolf.stackexchange.com/questions/112439
复制相似问题