hzzh 发表于 2004-5-8 17:31:00

这个题目不容易呢,好多高手都出马了,不错不错,不佩服的不行。
来了点好奇心,对游侠无极限 的程序运行了一下
算法还没有看懂,为什么结果会都是重复的?

请输入4个数(用回车或者空格分开):5 5 8 8
5*5-8/8
5*5-8/8
5*5-8/8
5*5-8/8
总计4个式子

另外程序有一点点小错误:

int EnumFormula(int *FormulaNum,int num,int sum)
{
    int result = 0;
   int *NumSym = (int*)calloc(num+4,sizeof(int));         //操作数运算符组成的可枚举组
    int *Formula = (int*)calloc(num*2-1,sizeof(int));    //波兰表达式储存空间
    int *temp = (int*)calloc(num*2,sizeof(int));      //波兰表达式的代号表示空间
    int i,j;
    for(i=0;i<num;i++) NumSym = FormulaNum;    //载入进行穷举的操作数
    for(j=0;j<5;j++) NumSym = -j-1;            //载入进行穷举的运算符号

前面定义:   int *NumSym = (int*)calloc(num+4,sizeof(int));         //操作数运算符组成的可枚举组
后来二句:
    for(i=0;i<num;i++) NumSym = FormulaNum;    //载入进行穷举的操作数
    for(j=0;j<5;j++) NumSym = -j-1;            //载入进行穷举的运算符号
当num=4时,经过第一个for,i=4,第二个for中 NumSym, i+j=8,NumSym越界了

唐明 发表于 2004-5-8 18:11:00

‘最后一次发代码
<html>
<head>
<script language="VBScript" type="text/VBScript">

dim o(3)''4个操作数
dim s(3)''操作数串
dim p(3)''当前运算之前的运算
dim oo(3,1)''当前运算之前的运算的前后两个操作书

function try(n)
dim opp(3)
dim spp(3)
dim ppp(3)
dim oopp(3,1)''用来保存相应的原始数据
for i=0 to n
    opp(i)=o(i)
    spp(i)=s(i)
    ppp(i)=p(i)
    oopp(i,0)=oo(i,0)
    oopp(i,1)=oo(i,1)
next
for i=1 to n
    for ii=0 to 4
      if ii=2 then ii=ii+1
      for iii=0 to n
      o(iii)=opp(iii)
      s(iii)=spp(iii)
      p(iii)=ppp(iii)
      oo(iii,0)=oopp(iii,0)
      oo(iii,1)=oopp(iii,1)''尝试之前先还原
      next
      if doop(ii,i-1) then
      if n=1 then
          if (abs(24-o(0))<0.0001) and (instr(list1.innertext,s(0))=0) then
            s(0)=left(s(0)+space(10),17)
            list1.innertext=list1.innertext+s(0)
          end if
      else
          try(n-1)'类似地跪
      end if
      end if
    next
next
end function


function doop(op,con)
'             ↑进行的运算
'               ↑这次运算的位置

'p(con)         之前的运算
'p(con+1)       之后的运算

'oo(con,0)      之前的运算的前操作书
'oo(con,1)      之前的运算的后操作书

'oo(con+1,0)    之后的运算的前操作书
'oo(con+1,1)    之后的运算的后操作书

'o(con)         现在将要进行的运算的前操作数
'o(con+1)       现在将要进行的运算的后操作数

's(con)         现在将要进行的运算的前操作数串
's(con+1)       现在将要进行的运算的后操作数串

'下面的例子前面部分数不允许的
'12/12*12+12=12+12/12*12
'(2+1)*(6+2)=(6+2)*(2+1)
'(6+2)*(3/1)=(6+2)*3*1=3*1*(6+2)
'6*4/4*4=6/4*4*4
'6*4+4-4=6*4-4+4
'4*(4+4)-8=(4+4)*4-8
'12*3/2+6=12/2*3+6
'.............等等差不多的狮子都认为是相同的
'结果的重复数量不是很多,但也可能漏掉了不少,要多多修改才行

'12/12*(13+11)<>(13+11)/12*12
'应为前面一个是1*24,后面是24*12/12,计算的顺序不同数值不同,但是12*(13+11)/12=12/12*(13+11)<>(13+11)*12/12'最后那个是不允许的(p(con)=op-1)就是乘法之后有计算过的除法
'...........


'代码设定的运算级别
'       "+"0
'       "-"1
'       "*"3
'       "/"4
'"无运算"6

doop=true
''************************
''这是为了避免一个奇怪的bug
''这次避免了2,2,2,3问题
i=len(s(0))
iii=0
for i=i to 1 step -1
    ii= mid(s(0),1)
    if ii>="0" and ii<="0" then iii=iii+1
next
if iii=len(sp(0)+sp(1)+sp(2)+sp(3)) then
    doop=false
    exit function
end if
''************************

if (p(con)=op-1) or (abs(p(con+1)-op)<2) or ((p(con)=op) and (oo(con,1)<o(con+1))) then
    '把恩代码中下列情况是不允许的
    '"+"/"-"之厚有机酸过的"+"/"-"
    '"*"/"/"之厚有机酸过的"*"/"/"比如 5*(2*9)是非法的
    '就是abs(p(con+1)-op)<2

    '"-"之前有计算过的"+"
    '"/"之前有计算过的"*"
    '就是(p(con)=op-1)

    '"+"/"-"/"*"/"/"之前是与它相同的运算,而之前的运算的后操作数<现在将要进行的运算的后操作数
    '就是(p(con)=op) and (oo(con,1)<o(con+1))
   
    doop=false
    exit function
end if


'"+"/"-"/"/"的前操作数<后操作数
'"-"/"*"/"/"的前操作数=0
'"*"/"/"后操作数=0
'"/"后操作数=1

'"+"之前运算前操作书<之后运算前操作书
'"+"之前运算前操作书=之后运算前操作书且'"+"之前运算后操作书<之后运算后操作书


'"*"的前操作数<后操作数且之前和将要进行运算的级别之差>1
'"+"/"*"之前运算级别<之后运算级别且之后运算级别<>6
'if ((oo(con,0)<oo(con+1,0)) or ((oo(con,0)=oo(con+1,0)) and (oo(con,1)<oo(con+1,1)))) and (abs(p(con)-p(con+1))<2) then
'这句子描述比较麻烦

'"/"

select case op
case 0
    if (o(con)<o(con+1)) or (p(con)<p(con+1) and p(con+1)<>6) or (p(con)>p(con+1) and p(con)=6) or (oo(con,0)<oo(con+1,0) or ((oo(con,0)=oo(con+1,0)) and (oo(con,1)<oo(con+1,1)))) then
      doop=false
      exit function
    end if
    if p(con) > 2 then oo(con,0)=o(con)'更新前操作数
    '计算
    o(con)=o(con)+o(con+1)
    s(con)=s(con)+"+"+s(con+1)
case 1
    if (o(con)<o(con+1)) or o(con)=0 then
      doop=false
      exit function
    end if
    if p(con) > 2 then oo(con,0)=o(con)'更新前操作数
    '计算
    o(con)=o(con)-o(con+1)
    s(con)=s(con)+"-"+s(con+1)
case 3
    ''if (o(con)<o(con+1)) and (abs(p(con)-p(con+1))<2 ) then
    ''*************************************↓修改
    if (o(con)<o(con+1)) and (abs(p(con)-op)>1)then
      doop=false
      exit function
    end if
    if (o(con)=0) or (o(con+1)=0) or ((p(con)<p(con+1)) and (p(con+1)<>6)) then
      doop=false
      exit function
    end if
    if ((oo(con,0)<oo(con+1,0)) or ((oo(con,0)=oo(con+1,0)) and (oo(con,1)<oo(con+1,1)))) and abs(p(con)-p(con+1))<2 then
      doop=false
      exit function
    end if
    if p(con) < 2 then'更新前操作数和括号
      oo(con,0)=o(con)
      s(con)="("+s(con)+")"
    end if
    if p(con+1) < 2 then s(con+1)="("+s(con+1)+")"'括号
    '计算
    o(con)=o(con)*o(con+1)
    s(con)=s(con)+"*"+s(con+1)

case 4
    if (o(con)<o(con+1)) or (o(con)=0) or (o(con+1)<2) then
      doop=false
      exit function
    end if
    if p(con) < 2 then'更新前操作数和括号
      oo(con,0)=o(con)
      s(con)="("+s(con)+")"
    end if
    if p(con+1) < 2 then s(con+1)="("+s(con+1)+")"'括号
    '计算
    o(con)=o(con)/o(con+1)
    s(con)=s(con)+"/"+s(con+1)
case else
    doop=false
    exit function
end select
oo(con,1)=o(con+1)'更新后操作数
p(con)=op'更新运算
'每次运算会减少操作数,后面数据向前移动1位
for i=con+1 to 2
    o(i)=o(i+1)
    s(i)=s(i+1)
    p(i)=p(i+1)
    oo(i,0)=oo(i+1,0)
    oo(i,1)=oo(i+1,1)
next
end function

dim oip(3)
dim sp(3)
dim pp(3)
dim oop(3,1)

function init()
dim mle
list1.innertext=""
for i=0 to 3
    if textx(i).value="" then exit function
    oip(i)=textx(i).value-0
    sp(i)=cstr(oip(i))
    pp(i)=6
    oop(i,0)=oip(i)
    oop(i,1)=oip(i)
next
''修正过的排列组合穷据
dim freeo(3),f(3)
For p0 = 0 To 3
    f(0)=p0
    For p1 = 0 To 2
      foo=0
      for i=0 to 3
      if i<>p0 then
          freeo(foo)=i
          foo=foo+1
      end if
      next
      f(1)=freeo(p1)
      For p2 = 0 To 1
      foo=0
      for i=0 to 3
      if i<>p0 and i<>f(1) then
          freeo(foo)=i
          foo=foo+1
      end if
      next
      f(2)=freeo(p2)
      p3 = 0
      foo=0
      for i=0 to 3
      if i<>p0 and i<>f(1) and i<>f(2) then
          freeo(foo)=i
          foo=foo+1
      end if
      next
      f(3)=freeo(p3)
      for i=0 to 3
      o(i)=oip(f(i))
      s(i)=sp(f(i))
      p(i)=pp(f(i))
      oo(i,0)=oop(f(i),0)
      oo(i,1)=oop(f(i),1)
       next
       if instr(mle,s(0)+s(1)+s(2)+s(3)+";") =0 then
         mle=mel+s(0)+s(1)+s(2)+s(3)+";"
         try(3)
       end if
      next
    next
next
if list1.innertext="" then list1.innertext="无解"
end function
</script>

</head>
<body>
<p>
<input type="button" id="testme" value="Try" onClick="init">
</p>
<p>
<input type="text" id="textx" style="width:30">
<input type="text" id="textx" style="width:30">
<input type="text" id="textx" style="width:30">
<input type="text" id="textx" style="width:30">
</p>
<p>
<textarea id="list1" cols="17" style="height:300" rows="1"></textarea>
</p>
</body>
</html>
[此贴子已经被作者于2004-5-8 18:20:59编辑过]

yzhlinux 发表于 2004-5-8 18:42:00

佩服佩服,不佩服不行了,
代码越来越工整,注释越来越详细实用,
真是长江后浪推前浪啊,前浪死在沙滩上

游侠无极限 发表于 2004-5-8 21:11:00

以下是引用hzzh在2004-5-8 17:31:09的发言:
这个题目不容易呢,好多高手都出马了,不错不错,不佩服的不行。
来了点好奇心,对游侠无极限 的程序运行了一下
算法还没有看懂,为什么结果会都是重复的?

请输入4个数(用回车或者空格分开):5 5 8 8
5*5-8/8
5*5-8/8
5*5-8/8
5*5-8/8
总计4个式子

另外程序有一点点小错误:

int EnumFormula(int *FormulaNum,int num,int sum)
{
   int result = 0;
      int *NumSym = (int*)calloc(num+4,sizeof(int));         //操作数运算符组成的可枚举组
   int *Formula = (int*)calloc(num*2-1,sizeof(int));    //波兰表达式储存空间
   int *temp = (int*)calloc(num*2,sizeof(int));      //波兰表达式的代号表示空间
   int i,j;
   for(i=0;i<num;i++) NumSym = FormulaNum;    //载入进行穷举的操作数
   for(j=0;j<5;j++) NumSym = -j-1;            //载入进行穷举的运算符号

前面定义:   int *NumSym = (int*)calloc(num+4,sizeof(int));         //操作数运算符组成的可枚举组
后来二句:
   for(i=0;i<num;i++) NumSym = FormulaNum;    //载入进行穷举的操作数
   for(j=0;j<5;j++) NumSym = -j-1;            //载入进行穷举的运算符号
当num=4时,经过第一个for,i=4,第二个for中 NumSym, i+j=8,NumSym越界了



真是太感谢了,想必释放错误是由于这个越界啊
至于重复,其实是这样的:
你输入的 5 5 8 8,在穷举中,第一个5和第二个5不等价,同样第三个8和第四个8不等价,所以结果是
5(1)*5(2)-8(3)/8(4)
5(2)*5(1)-8(3)/8(4)
5(1)*5(2)-8(4)/8(3)
5(2)*5(1)-8(4)/8(3)

唐明 发表于 2004-5-9 12:21:00

给游侠无极限
定义
操作数:v1,v2,v3,v4   就是4个数字
运算次序:px,p1,p2,p3,px计算的顺序,比如9*9*9*9是6,1,2,3,6 || 5*(8+9)/3是6,3,1,2,6等等
    运算:ox,o1,o2,o3,ox进行的计算
操作数串:s1,s2,s3,s4
特征马:v1-o1-v2-v12-o2-v3-v123-o3-v4这是按照p1,p2,p3的次序且v1>v2,v12>v3,v123>v4

次序px=6
运算ox="="

1)次序为 : "()" > "/" > "*" > "-" > "+"
相应级别:   5   4   3   1   0
2)检查前面 / 后面的运算,如果与现在将要进行的相同则继续检查前"面"生成的特征马的"后"面一个操作数/"后"面生成的特征马的"前"面一个操作数,"前"面生成的特征马的"后"面一个操作数>现在的"后"操作数 / "后"面生成的特征马的"前"面一个操作数>现在的"前"操作数,则跳出运算   (5月13日注:这规则是在前后都有计算过的运算时)
3)"+"/"*"作运算时"前"面的运算级别与现在将要进行的"相"同则"前"面的运算的"后"操作数必须是>=现在将要进行运算的"后"操作数,如果不符合则跳出运算
4.1)"/" / "*"检查前/后面的运算,小雨当前运算级别超过1,则在相应操作数串上+()
4.2)"/"检查"后"面的运算级别<=4,则在相应操作数串上+()   如果认为/(a/b)=/b*a/(a*b)=/a/b 则"后"面的运算级别=4或=3跳出运算
4.3)"*"检查"后"面的运算级别<3或=4,则在相应操作数串上+()
4.4)"-"检查"后"面的运算级别<=1,则在相应操作数串上+()   如果认为-(a-b)=-b+a-(a+b)=-b-a 则"后"面的运算级别=1或=0跳出运算
5)第2,3次运算结束时这次运算的运算代替次序在他之前的运算的运算
6)王成一个表达时的计算时应当分别保存特征马和最总的操作数串(完整的表达式串)在一个容器之中,已经存在相同的特征马则应该放弃现在的特征马和最总的操作数串(完整的表达式串)
7)如果认为*1和/1是相同的则两者只能选一,(禁止(当...时跳出运算)*/1或/1中的一个)或(*/1或/1中的一个生成特征妈时转换为对方)
8.1)为了避免6+3+2和6+(3+2)6+3-2和6+(3-2)...重复,"+"后面有"+"/"-"跳出运算
8.2)为了避免6*3*2和6*(3*2)6*3/2和6*(3/2)...重复,"*"后面有"*" / "/"跳出运算

一个特征马的存储形式:char={{v1,o1,v2,o2,v3,o3,v4},...}

这里所有跳出运算都可以通过改变前后的特征马而不跳出运算,但是那样很麻烦
这个产生特真马的前提就是我的代码(规则是比较乱的)的前提,这里只是描述的相对清楚,特征马就是我的代码中允许的表达时的计算过程,所以我的这个铁自可能是多余的


****************************************
例子1:
次序左右的6
运算左右的=
省略了
****************************************
操作数:v1,v2,v3,v4
运算次序:   p1,p2,p3
    运算:   o1,o2,o3
操作数串:v1,v2,v3,v4

下面是计算
原操作数:   v1,v2,v3,v4
一次运算:      v12 ,v3,v4
运算次序:       px,p2,p3
    运算:       ox,o2,o3
操作数串: v1p1v2,v3,v4,v4

二次运算:          v123,v4
运算次序:      px,px,p3
    运算:      ox,ox,o3
操作数串: v1p1v2p2v3,v4,v4,v4

三次运算:               v1234
运算次序:            px,px,px
    运算:            ox,ox,ox
操作数串: v1p1v2p2v3p3v4,v4,v4,v4

下面是特征马
v1-o1-v2-v12-o2-v3-v123-o3-v4
这是按照p1,p2,p3的次序且v1>v2,v12>v3,v123>v4

最总的操作数串(完整的表达式串)
v1p1v2p2v3p3v4
****************************************


****************************************
例子1的实例:
次序左右的6
运算左右的=
省略了
****************************************
操作数:2 ,2 ,3 ,2
运算次序:   1 ,2 ,3
    运算:   + ,* ,*
操作数串:2 ,2 ,2 ,2
就是:      (2+2)*3*2

下面是计算
原操作数:2 ,2 ,3 ,2
一次运算:    4 ,3 ,2
运算次序:   6 ,2 ,3
    运算:   + ,* ,*       生成2-+-2
操作数串:2+2,3,2,2

二次运算:       12,2
运算次序:   6 ,6 ,3
    运算:   * ,* ,*       生成4-*-3
操作数串:(2+2)*3,2,22   加上括号

三次运算:         24
运算次序:   6 ,6 ,6
    运算:   * ,* ,*    生成12-*-2
操作数串:(2+2)*3*2,2,2,2

下面是特征马
2-+-2-4-*-3-12-*-2
按照1,2,3(就是次序)的顺序

最总的操作数串(完整的表达式串)
(2+2)*3*2
****************************************

再比如2*(2+2)*3
特征马
2-+-2-?-?-?-?-?-?
后面跳出了计算,这个特征马/最总的操作数串(完整的表达式串)应当放弃
和(2+2)*3*2不会重复

再比如6,6,6,6
可以生成的特征马
6-+-6-12-+-6-18-+-6
6-*-6-36---6-30---6

再比如12,12,12,12
可以生成的特征马
12-+-12-24-/-12-2-*-12
12-/-12-1-*-12-12-+-12
12-+-12-12-/-12-1-*-24












[此贴子已经被作者于2004-5-12 19:06:09编辑过]

游侠无极限 发表于 2004-5-11 12:15:00

不太明白,比如
1*2*3*4
2*4*3*1
2*1*3*4
2/(1/(3*4))

的特征码是多少呢

唐明 发表于 2004-5-12 19:03:00

2)检查前面 / 后面的运算,如果与现在将要进行的相同则继续检查前"面"生成的特征马的"后"面一个操作数/"后"面生成的特征马的"前"面一个操作数,"前"面生成的特征马的"后"面一个操作数>现在的"后"操作数 / "后"面生成的特征马的"前"面一个操作数>现在的"前"操作数,则跳出运算   (5月13日注:这规则是在前后都有计算过的运算时)
                                                                                       ↓修改                        ↓      (5.13.2004修改)
3)"+"/"*"作运算时"前"面的运算级别与现在将要进行的"相"同则"前"面的运算的"后"操作数必须是>=现在将要进行运算的"后"操作数,如果不符合则跳出运算

4.2)"/"检查"后"面的运算级别<=4,则在相应操作数串上+()   如果认为/(a/b)=/b*a/(a*b)=/a/b 则"后"面的运算级别=4或=3跳出运算    (我认为是相等的)

8.2)为了避免6*3*2和6*(3*2)6*3/2和6*(3/2)...重复,"*"后面有"*" / "/"跳出运算

更见规则,你给出的狮子只有4*3*2*1是合法的,其他狮子舞发生成特征马
      4*3*2*1
次序:1,2, 3
运算*(3) ,*(3),*(3)括号内是级别
4*3=12    -> 4-3-3    结果12
12*2=24-> 12-*-2结果24
24*1=24-> 24*1    结果24
                   ↓      ↓         ↓都是级别
最总特征马4-3-3-12-3-2-24-3-1
               4*3   12*2   42*1

其他可以得到的特征吗(还有其他我原来里出的规则)
4-0-2-3-0-1-6-3-4   (4+2)*(3+1)
3-0-2-5-0-1-6-3-4       (3+2+1)*4

游侠无极限 发表于 2004-5-13 20:41:00

我想了一个特征码:
取每个操作数所进行的运算符
即:
1*2*3*4
各个操作数进行的运算分别为:
1 *
2 *
3 *
4 *
特征码为****
(1+3)*(2+4)为
1+
3+
2+
4+
为++++
2/(1/(3*4))
2/
1/
3*
4*
为**//
隐含特征码为四个操作数和运算结果,因为相同,所以除去
目的是消除因加法和乘法交换率引起的重复
不知道有没有漏掉的,或者误除的


[此贴子已经被作者于2004-5-13 20:43:35编辑过]
页: 1 2 [3]
查看完整版本: 有没有人想过怎么用计算机来实现24点