Nol*_*rin 36 algorithm math code-golf permutation
这是一项任务,灵感来自着名的英国电视游戏节目Countdown.即使不了解游戏,挑战也应该非常明确,但随时可以要求澄清.
如果你想看到这款游戏的动态片段,请查看此YouTube视频片段.它拥有1997年美妙的已故理查德怀特利.
您将获得6个数字,从集{1,2,3,4,5,6,8,9,10,25,50,75,100}中随机选择,以及100到999之间的随机目标数.目的是使用六个给定的数字和四个常用的算术运算(加法,减法,乘法,除法;遍及有理数)来生成目标 - 或尽可能接近任一侧.每个数字最多只能使用一次,而每个算术运算符可以使用任意次数(包括零).请注意,使用多少个数并不重要.
编写一个函数,它接受目标数和6个数字的集合(可以表示为列表/集合/数组/序列),并以任何标准数字符号(例如中缀,前缀,后缀)返回解决方案.该函数必须始终将最接近的结果返回给目标,并且必须在标准PC上运行最多1分钟.请注意,在存在多个解决方案的情况下,任何单个解决方案就足够了.
例子:
{50,100,4,2,2,4},目标203,
例如100*2 + 2 +(4/4)(精确),
例如(100 + 50)*4*2 /(4 + 2)(精确)
{25,4,9,2,3,10},目标465
例如(25 + 10 - 4)*(9*2-3)(确切)
{9,8,10,5,9,7},目标241
例如((10 + 9)*9*7)+ 8)/ 5 (确切)
{3,7,6,2,1,7},目标824
例如((7*3)-1)*6-2)*7 (= 826;偏2)
除了在问题陈述中提到的,没有进一步的限制.您可以使用任何标准语言编写函数(不需要标准I/O).一如既往的目标是用最少数量的代码来解决任务.
说,我可能不会简单地用最短的代码接受答案.我还将关注代码的优雅和算法的时间复杂度!
当我找到空闲时间时,我正在尝试使用F#解决方案 - 当我有东西时会将它发布在这里!
请以下列格式发布所有答案,以便于比较:
语言
字符数:???
完全混淆的功能:
Run Code Online (Sandbox Code Playgroud)(code here)清除(理想评论)功能:
Run Code Online (Sandbox Code Playgroud)(code here)关于算法/聪明的快捷方式的任何注释.
Kei*_*all 11
字符数:548 482 425 421 416 413 408
from operator import *
n=len
def C(N,T):
R=range(1<<n(N));M=[{}for i in R];p=1
for i in range(n(N)):M[1<<i][1.*N[i]]="%d"%N[i]
while p:
p=0
for i in R:
for j in R:
m=M[i|j];l=n(m)
if not i&j:m.update((f(x,y),"("+s+o+t+")")for(y,t)in M[j].items()if y for(x,s)in M[i].items() for(o,f)in zip('+-*/',(add,sub,mul,div)))
p|=l<n(m)
return min((abs(x-T),e)for t in M for(x,e)in t.items())[1]
Run Code Online (Sandbox Code Playgroud)
你可以这样称呼它:
>>> print C([50, 100, 4, 2, 2, 4], 203)
((((4+2)*(2+100))/4)+50)
Run Code Online (Sandbox Code Playgroud)
在旧PC上给出的示例大约需要半分钟.
这是评论版:
def countdown(N,T):
# M is a map: (bitmask of used input numbers -> (expression value -> expression text))
M=[{} for i in range(1<<len(N))]
# initialize M with single-number expressions
for i in range(len(N)):
M[1<<i][1.0*N[i]] = "%d" % N[i]
# allowed operators
ops = (("+",lambda x,y:x+y),("-",lambda x,y:x-y),("*",lambda x,y:x*y),("/",lambda x,y:x/y))
# enumerate all expressions
n=0
while 1:
# test to see if we're done (last iteration didn't change anything)
c=0
for x in M: c +=len(x)
if c==n: break
n=c
# loop over all values we have so far, indexed by bitmask of used input numbers
for i in range(len(M)):
for j in range(len(M)):
if i & j: continue # skip if both expressions used the same input number
for (x,s) in M[i].items():
for (y,t) in M[j].items():
if y: # avoid /0 (and +0,-0,*0 while we're at it)
for (o,f) in ops:
M[i|j][f(x,y)]="(%s%s%s)"%(s,o,t)
# pick best expression
L=[]
for t in M:
for(x,e) in t.items():
L+=[(abs(x-T),e)]
L.sort();return L[0][1]
Run Code Online (Sandbox Code Playgroud)
它通过详尽列举所有可能性来工作.它有点聪明,如果有两个具有相同值的表达式使用相同的输入数字,它会丢弃其中一个.它在考虑新组合方面也很聪明,使用M中的索引快速修剪共享输入数字的所有潜在组合.
字符数:361 350 338 322
完全混淆的功能:
m=map
f=toRational
a%w=m(\(b,v)->(b,a:v))w
p[]=[];p(a:w)=(a,w):a%p w
q[]=[];q(a:w)=[((a,b),v)|(b,v)<-p w]++a%q w
z(o,p)(a,w)(b,v)=[(a`o`b,'(':w++p:v++")")|b/=0]
y=m z(zip[(-),(/),(+),(*)]"-/+*")++m flip(take 2 y)
r w=do{((a,b),v)<-q w;o<-y;c<-o a b;c:r(c:v)}
c t=snd.minimum.m(\a->(abs(fst a-f t),a)).r.m(\a->(f a,show a))
Run Code Online (Sandbox Code Playgroud)
功能清晰:
-- | add an element on to the front of the remainder list
onRemainder :: a -> [(b,[a])] -> [(b,[a])]
a`onRemainder`w = map (\(b,as)->(b,a:as)) w
-- | all ways to pick one item from a list, returns item and remainder of list
pick :: [a] -> [(a,[a])]
pick [] = []
pick (a:as) = (a,as) : a `onRemainder` (pick as)
-- | all ways to pick two items from a list, returns items and remainder of list
pick2 :: [a] -> [((a,a),[a])]
pick2 [] = []
pick2 (a:as) = [((a,b),cs) | (b,cs) <- pick as] ++ a `onRemainder` (pick2 as)
-- | a value, and how it was computed
type Item = (Rational, String)
-- | a specification of a binary operation
type OpSpec = (Rational -> Rational -> Rational, String)
-- | a binary operation on Items
type Op = Item -> Item -> Maybe Item
-- | turn an OpSpec into a operation
-- applies the operator to the values, and builds up an expression string
-- in this context there is no point to doing +0, -0, *0, or /0
combine :: OpSpec -> Op
combine (op,os) (ar,as) (br,bs)
| br == 0 = Nothing
| otherwise = Just (ar`op`br,"("++as++os++bs++")")
-- | the operators we can use
ops :: [Op]
ops = map combine [ ((+),"+"), ((-), "-"), ((*), "*"), ((/), "/") ]
++ map (flip . combine) [((-), "-"), ((/), "/")]
-- | recursive reduction of a list of items to a list of all possible values
-- includes values that don't use all the items, includes multiple copies of
-- some results
reduce :: [Item] -> [Item]
reduce is = do
((a,b),js) <- pick2 is
op <- ops
c <- maybe [] (:[]) $ op a b
c : reduce (c : js)
-- | convert a list of real numbers to a list of items
items :: (Real a, Show a) => [a] -> [Item]
items = map (\a -> (toRational a, show a))
-- | return the first reduction of a list of real numbers closest to some target
countDown:: (Real a, Show a) => a -> [a] -> Item
countDown t is = snd $ minimum $ map dist $ reduce $ items is
where dist is = (abs . subtract t' . fst $ is, is)
t' = toRational t
Run Code Online (Sandbox Code Playgroud)
关于算法/聪明的快捷方式的任何注释:
z将列表中的单子,而不是Maybe为ops做.reduce 多次生成一些答案,以便轻松包含较少术语的解决方案.y部分地根据其自身来定义.Rational值计算.Golf'd代码将缩短17个字符,如果计算得更快,则更快Double.onRemainder影响pick和之间的结构相似性pick2.高尔夫版驱动程序:
main = do
print $ c 203 [50, 100, 4, 2, 2, 4]
print $ c 465 [25, 4, 9, 2, 3, 10]
print $ c 241 [9, 8, 10, 5, 9, 7]
print $ c 824 [3, 7, 6, 2, 1, 7]
Run Code Online (Sandbox Code Playgroud)
按时间运行(每个结果仍然不到一分钟):
[1076] : time ./Countdown
(203 % 1,"(((((2*4)-2)/100)+4)*50)")
(465 % 1,"(((((10-4)*25)+2)*3)+9)")
(241 % 1,"(((((10*9)/5)+8)*9)+7)")
(826 % 1,"(((((3*7)-1)*6)-2)*7)")
real 2m24.213s
user 2m22.063s
sys 0m 0.913s
Run Code Online (Sandbox Code Playgroud)