西西河

主题:【原创】将24进行到底 -- 泰让

共:💬28 🌺15
分页树展主题 · 全看首页 上页
/ 2
下页 末页
      • 家园 print函数

        1 3 5 4 有12个结果,4 4 7 7 有4个结果, 1 5 5 5 有6个结果。

        void print(vector<double> A,vector<int> op,int i)

        {

        if (i==4||i==5||i==6||i==7) { swap(A[0],A[1]); i=i-4; }

        switch (i)

        {

        case 0 : cout<<"(("<<A[0]; print_op(op[0]); cout<<A[1]<<')';

        print_op(op[1]); cout<<A[2]<<')';

        print_op(op[2]); cout<<A[3]; break;

        // ((A#B)#C)#D

        case 1 : cout<<A[3]; print_op(op[2]);

        cout<<"(("<<A[0]; print_op(op[0]); cout<<A[1]<<')';

        print_op(op[1]); cout<<A[2]<<')'; break;

        // D#((A#B)#C)

        case 2 : cout<<'('<<A[2]; print_op(op[1]);

        cout<<'('<<A[0]; print_op(op[0]); cout<<A[1]<<"))";

        print_op(op[2]); cout<<A[3]; break;

        // (C#(A#B))#D

        case 3 : cout<<A[3]; print_op(op[2]);

        cout<<'('<<A[2]; print_op(op[1]);

        cout<<'('<<A[0]; print_op(op[0]); cout<<A[1]<<"))"; break;

        // D#(C#(A#B))

        }

        }

        void PRINT(vector<double> A,vector<int> op,int i)

        {

        if (i==4||i==5||i==6||i==7) swap(A[0],A[1]);

        if (i==2||i==3||i==6||i==7) swap(A[2],A[3]);

        if (i%2==0)

        {

        cout<<'('<<A[0]; print_op(op[0]); cout<<A[1]<<')';

        print_op(op[2]);

        cout<<'('<<A[2]; print_op(op[1]); cout<<A[3]<<')';

        // (A#B)#(C#D)

        }

        else

        {

        cout<<'('<<A[2]; print_op(op[1]); cout<<A[3]<<')';

        print_op(op[2]);

        cout<<'('<<A[0]; print_op(op[0]); cout<<A[1]<<')';

        // (C#D)#(A#B)

        }

        }

    • 家园 泰让、highway兄请来看看

      我的下门考试已经看到了曙光,所以继续学习两位老兄的Codes。

      俺的程序改用了递归,输出依然还是问题。为了学习方便,两位老兄是否考虑几个验算输出。即:任意4个数,如果有解,最多有多少个解?或者说,A+B,B+A算2个解,简单的全排列一下,总共有多少个解才不会有漏掉的排列?

      俺用泰让兄的程序算了一下,1 3 5 4(52个解),1 5 5 5(12个解),4 4 7 7(8个解)。

      仔细考虑了一下,这个想法完全没有必要!

      • 家园 今天晚上我把souce code给你打包发过去。

        你可以自己在上面根据你的需要更改。怎么样?

        • 家园 发我一份呀

          想学习学习multi-threading部分

          • 家园 哈,你们论剑的时候别忘了俺!

            俺这个算法还没搞定,又是多线程,哎,谁教俺面壁是出了名的慢。不过,嘿嘿,出题权还在俺!

          • 家园 那就贻笑大方了。

            这里是多线程部分。用了一些JDK 5.0以后的new feature,所以可能看起来有些“异怪”。加了很简单一些注释,希望能有点小小帮助。

            package game;

            import java.util.Map;

            import java.util.Set;

            import java.util.concurrent.ConcurrentHashMap;

            import java.util.concurrent.CountDownLatch;

            import java.util.concurrent.ExecutorService;

            import java.util.concurrent.Executors;

            /**

            * This one has ability to scale up to multiple CPUs. It is

            * subclass of plain version of PokerGame

            */

            public class PokerGameMT extends PokerGame

            {

            //uses the latest Java concurrent feature--Thread pool

            private final ExecutorService pool =

            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

            private final Object OBJ = new Object();

            /**

            * Inner class, do real calculation job. Basically it is just another

            * way to execute calculate() function defined in base class

            */

            private class Runner implements Runnable

            {

            private int[] set;

            private Map<String, Object> solutions;

            private CountDownLatch latch;

            Runner(int[] set, Map<String, Object> solutions, CountDownLatch latch)

            {

            this.set = set;

            this.solutions = solutions;

            this.latch = latch;

            }

            public void run()

            {

            for (Operator[] opCombo : opCombos)

            {

            Set<int[]> calcSeq = PokerGameMT.this.getCalcSequence(opCombo);

            for (int[] seq : calcSeq)

            {

            String solution = PokerGameMT.this.calculate(set, opCombo, seq);

            if (solution != null)

            {

            solutions.put(solution, OBJ);

            }

            }

            }

            latch.countDown();

            }

            }

            /**

            * Given a number of poker, find the total sulotions

            */

            protected Set<String> findSolution(int[] nums)

            {

            /**

            * use ConcurrentHashMap is for performance reason. Otherwise,

            * Set<String> would be just fine.

            */

            Map<String, Object> solutions = new ConcurrentHashMap<String, Object>();

            Map<Integer, int[]> workingSet = new ConcurrentHashMap<Integer, int[]>();

            int[] aPokeSet = new int[nums.length];

            for(int[] set : this.permutations)

            {

            for (int i = 0; i < nums.length; i++)

            {

            aPokeSet[i] = nums[set[i]];

            }

            /**

            * avoid duplication calculation. Only different set

            * is sent to runner threads.

            */

            int code = this.getPokeHashCode(aPokeSet);

            if (!workingSet.containsKey(code))

            {

            int[] aCalcset = new int[nums.length];

            System.arraycopy(aPokeSet, 0, aCalcset, 0, nums.length);

            workingSet.put(code, aCalcset);

            }

            }

            int totalPerm = workingSet.size();

            //new feature of JDK 5.0 or after. Just like old join()

            CountDownLatch latch = new CountDownLatch(totalPerm);

            //submit job to runner thread

            for(int[] set : workingSet.values())

            {

            pool.execute(new Runner(set, solutions, latch));

            }

            try

            {

            //wait for runner finish calculation

            latch.await();

            }catch(InterruptedException ex)

            {

            ex.printStackTrace();

            }

            return solutions.keySet();

            }

            /**

            * tear down the thread pool

            */

            protected void close()

            {

            super.close();

            pool.shutdownNow();

            }

            /**

            * Constructor. first one specify poke number and

            * second one specify expected value, 24 for instance

            * @param pokeNum

            * @param answer

            */

            public PokerGameMT(int pokeNum, int answer)

            {

            super(pokeNum, answer);

            }

            }

    • 家园 【注释之1】总体原理

      首先说一下总体上的思路。其实很多朋友已经看出,如果想穷尽4张牌的所有可能的计算,需要找到一个排列方法,能够不遗漏的列举每个算式。运算的数好办,因为它们都只严格的出现1次,问题是对于运算符,简单的插入三个运算符在四个数的空隙中是不行的,因为要是我们加入括号改变运算顺序,其结果也就不同了。但是如果我们要考虑所有插入括号的方式,虽然仍然是可行,但问题就变的相当复杂了。

      好在算术表达式不仅仅有常见的中缀法,还有后缀法,或者称为逆波兰法(RPN)。这种方法先写两个参加运算的部分(可以是数,或者小一极的表达式)。简单的说就是原本用1+2来表示的运算,写成rpn就是1 2+,再比如说我们用中缀表示的算式(4-4/7)*7用后缀可以表示成4 4 7 / - 7 *。注意我们这里假定运算数间有可分辨的分隔,这样4 ,4 ,7三个数不至于同四百四十七混淆。

      这样表示有什么好处?第一:这么写以后可以不用括号表达所有算式,而如果我们把一个数看作一位,一个运算符也看作1位的话,这个表达式长度总是7位。第二:这样写后计算过程简单。对于有括号的四则运算的解析和运算其实是相当复杂的问题,编译原理中会有一章专门讲这个问题。对于RPN计算就简单很多,我们可以很机械的执行以下步骤:遇到运算数则压栈,如果遇到运算符则将栈顶两运算数进行相应操作。最后如果RPN没有语法错误的话,栈应该只剩下一个数,就是结果。

      回到原本的问题,我们现在要做的就是生成所有可能的RPN,然后运算之,检查它是否结果是24。如果我们用1到4代表输入的4张牌点,5到8代表加减乘除四种运算的话,问题就变成了我们如何生成一个7个数的组合,满足以下条件:

      1:一到四出现1次且仅出现一次

      2:五到八可以重复出现,也可以不出现

      3:在任何时候,一到四出现的总数,比五到八出现的次数多(这一条是保证生成的是合乎语法的RPN)

      生成了一个组合后,再用上面说的计算方法来算出结果,检查下是否是目标就可以了。当然注意运算过程中别发生除零的错误。

      • 家园 【注释之2】程序实现

        有了思路后编程就不是很难的事情,下面对代码本身解释一下:

        comp数组是存放组合的,op数组存放输入的四张牌点。validate函数计算已经生成的7位排列是否算出目标值,output把comp的逆波兰转化成熟悉的中缀表示法。注意这两个函数很象,只不过一个算浮点,另一个算字符串而已。

        expand和expandable是生成排列的主体,其中expand用递归的办法从左向右试探comp的每一位,如果7位全部填完则调用validate和output输出结果。而expandable函数则试探在特定的位置上是否可以放置特定的数。其中第一个循环判断条件1(运算数出现最多1次),第二个循环测试条件3(从左往右扫描的时候运算数总比运算符多)。当试探成功后则在comp数组相应位置放上这个数,由于是递归,回溯可以自动进行。

        程序应该可以在大多数c编译器上通过。我用的是windows 下的LCC和linux下的gcc。由于目标和牌数都定义成常量,所以容易改动,以解决更多张牌或者不同目标数的情况,也可以把它们定义成用户输入的变量。

        虽然我知道比较不同语言的程序运行速度意义不大,可还是忍不住大致做了下测试。对于跑完全部4张牌的情况,在赛扬2.4G Windows XP SP2.0下大概需要9秒钟左右。同样的Binary在另一台双核1.8G笔记本上速度约7秒。当然,程序没有用到任何多线程的东西,所以在双核下没有太多的优势。

    • 家园 还是烦请老兄再添点儿注解。。。

      还是烦请老兄再添点儿注解,这样应该更能领会得深一些谢谢了。

      另外,我这VC编译器上只能这样才行:59行:stack[i]=(char*)malloc(255*sizeof(char));

    • 家园 谢谢老弟的捧场。

      你的程序我编译运行了一下,四张牌是没有什么问题的。但是我牌数改为6,Target改为5188,程序就似乎没有解了。你能看一下这个问题吗?

      我改动了你程序中的两个常量

      #define TAGET 5188

      #define NOP 6

      这里是程序运行结果

      点看全图

      外链图片需谨慎,可能会被源头改

      • 家园 交换律和结合律

        我这段时间也再想这个问题

        算出结果不难,但是要考虑交换律和结合律还是要费点工夫

        楼主的程序显然没有考虑交换律的问题,屏幕上打印出的结果实际上是同一个

      • 家园 另外71行处理输出的时候漏了一对括号

        sprintf(tempstr,"%s+%s",stack[top-1],stack[top]);

        应该是

        sprintf(tempstr,"(%s+%s)",stack[top-1],stack[top]);

      • 家园 谢谢指出

        错误原因在第9行

        double op[4];

        这个magic number 4应该改成NOP

分页树展主题 · 全看首页 上页
/ 2
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河