Advertisement
rplantiko

Find the operations - with groovy

Oct 15th, 2011
647
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Groovy 8.43 KB | None | 0 0
  1. /* A "find-the-operations" problem
  2.  
  3.    Given the sequence
  4.    
  5.        1   2   3    4    5    6    7    8     9     =   2011
  6.        
  7.    Find the operations (+,-,*,/) to insert between the numbers.
  8.    Arbitrary parenthizing is allowed.
  9.    If there are solutions: How many?
  10.    
  11.    For the solution, I use the GPars framework http://gpars.codehaus.org/
  12.    for achieving multi-threaded execution
  13.    
  14.    The Master task determines the different ways of parenthizing. In the
  15.    example, we have C_8 = 1430 different ways to place the brackets.
  16.    C_8 = (2n)!/((n+1)*n!^2) is the eighth Catalan number.
  17.    
  18.    For each bracket pattern, the slave substitutes the 4 operations
  19.    at the 8 placeholders of the pattern. Thus, each slave performs
  20.    2^(2*8) evaluations.
  21.    
  22.    The part that needs the most computing power is the execution of
  23.    the computation (who wonders?)  
  24.    
  25.    In order to avoid losing hits due to precision problems, I use rational numbers.
  26.  
  27.    My final implementation in C++ improved performance by a factor of 10'000, showing that
  28.    the choice of language *is* important. Anyway, the Groovy implementation shows a lot
  29.    of tricks that can be played with this language. Its power is its expressiveness,    
  30.    approaching closely yet the "develop by config" vision. It is a very good choice for
  31.    coding located near a user interface. It is a not so good choice for computational
  32.    tasks like this one.
  33.  
  34.    See my github folder https://github.com/rplantiko/computeExpressions for the final
  35.    implementation in C++
  36.  
  37.    Requires groovy class RationalNumber, see http://pastebin.com/J2fhdMSP
  38.        
  39.  
  40. */
  41.  
  42. import groovyx.gpars.*
  43. import groovyx.gpars.actor.*
  44. import groovyx.gpars.actor.Actors.*
  45. import static groovyx.gpars.actor.Actors.actor
  46. import java.util.concurrent.CountDownLatch
  47.  
  48. // An Enum for the basic operations (+,-,*,/)
  49. import static Operation.*
  50.  
  51. //start the actors
  52. File logfile = new File(/D:\groovy\log.txt/)
  53.  
  54. def master = new CalculatorMaster(numberOfActors:8,size:9,expectedResult:2011,logfile:logfile)
  55. master.allOperations.each { println it }
  56. master.start()
  57. master.waitUntilDone()
  58.  
  59. println "\nDone"
  60.  
  61.  
  62. // Messages
  63. private final class Operations { int size; List<List> data; int expectedResult  }
  64. private final class Result { List<List> data }
  65.  
  66. //Worker actor
  67. final class CalculatorActor extends AbstractPooledActor {
  68.   void act() {
  69.     loop {
  70.       react {message ->
  71.         switch (message) {
  72.           case Operations:
  73.             Result r = new Result(data:[])
  74.             message.data.each {
  75.               List<List> result = new Combinator(
  76.                 message.size,
  77.                 it,
  78.                 new RationalNumber( message.expectedResult,1) ).findMatches()
  79.               if (result.size > 0) r.data.push( result )
  80.               }
  81.             reply r
  82.             }
  83.           }
  84.       }
  85.     }
  86.   }
  87.  
  88. //Master actor
  89. final class CalculatorMaster extends AbstractPooledActor {
  90.  
  91.     int expectedResult = 6
  92.     int size = 5
  93.     int numberOfActors = 5    
  94.     File logfile    
  95.    
  96.     private CountDownLatch startupLatch = new CountDownLatch(1)
  97.     private CountDownLatch doneLatch
  98.  
  99.     private int startTasks() {
  100.       List<List<Integer>> operations = getAllOperations()
  101.       int cnt = sendTasksToWorkers( operations )
  102.       doneLatch = new CountDownLatch(cnt)
  103.       operations.size
  104.     }
  105.  
  106.     private List createWorkers() {
  107.         return (1..numberOfActors).collect {new CalculatorActor().start()}
  108.     }
  109.    
  110.     public List<List<Integer>> getAllOperations() {
  111.       List<List<Integer>> operations = []
  112.       buildOperation( [], operations )
  113.       return operations      
  114.       }
  115.      
  116.     private void buildOperation( List<Integer> operation, List<List<Integer>> operations ) {
  117.       def k = operation.size()
  118.       def s = k > 0 ? operation.sum() : 0      
  119.       if (k < size - 2) {
  120.         (k+2).times {
  121.           if (s+it<k+2) {
  122. // copy this trunk       
  123.             ArrayList child = new ArrayList( operation )  
  124. // Add the current index           
  125.             child.push it
  126. // Recur to next lower level
  127.             buildOperation( child, operations )
  128.             }
  129.           }
  130.         }
  131.        else {
  132. // Lowest level reached - another operation is complete
  133.         assert size-s >= 1
  134.         operation.push( size-s-1 )     // The last index can be computed
  135.         operations.push( operation )   // Push to result
  136.         }          
  137.       }  
  138.  
  139.     private int sendTasksToWorkers(List<List> operations) {
  140.         List<AbstractPooledActor> workers = createWorkers()
  141.         int cnt = 0
  142.         operations.each {
  143.           workers[cnt % numberOfActors] << new Operations(size:size,expectedResult:expectedResult,data:[it])
  144.           cnt += 1
  145.         }
  146.         return cnt
  147.     }
  148.  
  149.     public void waitUntilDone() {
  150.         startupLatch.await()
  151.         doneLatch.await()
  152.     }
  153.  
  154.     void act() {
  155.         int numberOfOperations = startTasks()
  156.         startupLatch.countDown()
  157.         println "$numberOfOperations operations"
  158.         logfile.write "$numberOfOperations operations\n"
  159.         loop {
  160.             react {
  161.                 switch (it) {
  162.                     case Result:
  163.                         print "."
  164.                         it.data.each {
  165.                           if (it) println ""
  166.                           it.each {
  167.                             println it
  168.                             logfile << it << "\n"
  169.                             }
  170.                           }
  171.                         doneLatch.countDown()
  172.                 }
  173.             }
  174.         }
  175.     }
  176. }
  177.  
  178.  
  179. class Combinator {
  180.  
  181.   ArrayList allocation = []
  182.   final RationalNumber expectedResult
  183.   final int size
  184.   private acc = []
  185.   List<List> result = []
  186.   private final static Operation[] ops = [ PLUS, MINUS, TIMES, BY ];
  187.  
  188.   public Combinator( int size, ArrayList operations, RationalNumber expectedResult) {
  189.     assert operations.size == size - 1
  190.     assert operations.sum()  == size - 1
  191.     this.size = size
  192.     this.expectedResult = expectedResult
  193.     int numOpCount = 0
  194.     size.times {
  195.       allocation.push it + 1
  196.       if (it >= 1) {
  197.         numOpCount += operations[it-1]
  198.         assert numOpCount <= it
  199.         operations[it-1].times {
  200.           allocation.push ANY
  201.           }
  202.         }
  203.       }      
  204.     }
  205.    
  206.   public List findMatches() {
  207.    
  208.     final BigInteger differentAllocations = 1 << 2*(size-1)
  209.    
  210. // Look for the places with operations - needs to be done only once    
  211.     List<Integer> allocIndex = []
  212.     allocation.eachWithIndex { it, index ->
  213.       if (it instanceof Operation) {
  214.         allocIndex.push index
  215.         }
  216.       }
  217.     assert allocIndex.size == size - 1    
  218.    
  219.     differentAllocations.times { iAlloc ->
  220.       (size-1).times { i ->
  221.         allocation[allocIndex[i]] = ops[iAlloc & 0x3]
  222.         iAlloc = iAlloc >> 2
  223.         }
  224.       if (Calculator.evaluate(allocation).equals(expectedResult)) {
  225.         result.push new ArrayList(allocation)
  226.         }
  227.       }
  228.    
  229.     return result
  230.    
  231.     }
  232.  
  233.  
  234.   }
  235.  
  236.  
  237. class Calculator {
  238.   public static Object evaluate(ArrayList input) {
  239.     def exception = null
  240.     Operation x;
  241.     def stack = new Calculator.ExecutionStack()      
  242.     input.each {      
  243.       if (it instanceof Operation) {
  244.         stack.apply it
  245.         }
  246.       else {
  247.         stack.push new RationalNumber(it,1)
  248.         }
  249.       }
  250.     return stack.result        
  251.     }  
  252.  
  253.   static class ExecutionStack {
  254.     private Stack s = []
  255.     def apply = { op ->
  256.       def right = s.pop()
  257.       def left  = s.pop()
  258.       s.push op.apply( left, right )
  259.       }
  260.     def push = { arg -> s.push( arg ) }
  261.     public getResult() { s.pop() }
  262.     }
  263.    
  264.   }  
  265.      
  266. enum Operation {
  267.   PLUS({ a,b -> a+b }, '+'),
  268.   MINUS({ a,b -> a-b }, '-'),
  269.   TIMES({ a,b -> a*b }, '*'),
  270.   BY({ a,b -> a/b }, '/')
  271.   private final Closure op;
  272.   private final String glyph;
  273.   Operation(Closure op, String glyph) { this.op = op; this.glyph = glyph }
  274.   Operation(String glyph) { this.glyph = glyph }
  275.   def apply = { a,b -> op(a,b) }
  276.   public static final ANY = TIMES;
  277.   public String toString() { glyph }
  278.   }
  279.  
  280.  
  281. assert Calculator.evaluate([1,2,3,PLUS,PLUS]) == new RationalNumber( 6, 1 )
  282. assert Calculator.evaluate([1,2,PLUS,3,TIMES,6,4,MINUS,MINUS]) == new RationalNumber( 7, 1 )
  283. assert Calculator.evaluate([1,0,BY]) == RationalNumber.DIV_BY_ZERO
  284. assert Calculator.evaluate([2,1,MINUS]) == new RationalNumber( 1, 1 )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement