blob: e7f9bd0c008ede43f153e09d159bf04ee88adfa4 [file] [log] [blame]
/***************************************************************************
PART 1: PROGRAM
Pierre Ouellet ID 9009791
The problem solved by this program is stated as follows:
Given a set S of k nonzero natural numbers, labeled n1..nk,
a "total" t, which is also a nonzero natural number, find
an expression tree, whose leaves are taken from S, and whose
interior nodes are each labeled one of {+, -, *, /}, and that
evaluates to t. There is the additional restriction that any
subtree must evaluate to a natural number greater than 0.
Division is permitted only if the value of the left child
is exactly divisible by the value of the right child. Also,
numbers in S may not be used more times than they appear,
e.g. if S = { 2, 3, 3 }, "3" may be used at most twice.
The program uses a brute-force search, more precisely an
iterative deepening depth-first search. The search can be
viewed as an attempt to build the tree bottom-up: whenever
the value t is not present, we try to obtain it by "combining"
two available values, which are taken from the "work list",
which consists of those elements of S not yet used and the values
obtained by previous combinations. This is the equivalent of
creating a new root and making the two operands its children.
The work list is an array of integers, originally containing
the elements of S. When elements are combined, they're marked
used and the result of combining them (the result of evaluating
the new root) is added at the end of the list.
The combinations are saved in an array of Comb, to be printed out
when a solution is found.
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#define NOOP 0
#define ADD 1
#define SUB 2
#define MUL 3
#define DIV 4
typedef struct
{
int operand1, operand2;
int operation;
} Comb;
/* Global variables are used to save time during the search */
static int goal; /* the value of t explained above */
static int listLength; /* |S| */
static int *workList;
static Comb *combList;
static Comb *solution;
static int best = 0; /* value of best solution found yet */
static int dmax; /* maximal depth of search */
static int stopSearch; /* flag so we stop at first solution */
static int bestDepth = 0; /* depth of best solution; used if no exact
solution is found */
static int nbNodes; /* statistics: number of nodes examined
(number of calls to recSearch) */
/************************* INITIALIZATION STUFF *************************/
int *newWorkList( int length )
/* Create a "work list", which is an array of integers with length
elements
*/
{
int *newList = (int *) calloc( length, sizeof( int ) );
if( newList ) return newList;
else
{
fprintf( stderr, "Out of memory for work list\n" );
exit( 1 );
}
}
Comb *newCombList( int length )
/* Create a "combination list", which is to be used all the way through
the search. It contains the combinations attempted so far in the search.
*/
{
Comb *newList = (Comb *) calloc( length, sizeof( Comb ) );
if( newList ) return newList;
else
{
fprintf( stderr, "Out of memory for combination list\n" );
exit( 1 );
}
}
void initWorkList( int *workList, int *givenList, int length )
{
int i;
for( i = 0; i < length; i++ )
workList[i] = givenList[i];
}
void initCombList( Comb *combList, int length )
{
int i;
for( i = 0; i < length; i++ )
combList[i].operation = NOOP;
}
/************************* AUXILIARY FUNCTIONS *************************/
void saveSolution( Comb *sol, Comb *combList, int length )
/* Copy a sequence of combinations.
*/
{
int i;
for( i = 0; i < length; i++ )
{
sol[i].operand1 = combList[i].operand1;
sol[i].operand2 = combList[i].operand2;
sol[i].operation = combList[i].operation;
}
sol[length].operation = NOOP; /* End marker */
}
int calculate( Comb *comb )
/* Compute the value generated by a combination.
*/
{
switch( comb->operation )
{
case ADD: return comb->operand1 + comb->operand2;
case SUB: return comb->operand1 - comb->operand2;
case MUL: return comb->operand1 * comb->operand2;
case DIV: return comb->operand1 / comb->operand2;
default: return 0;
}
}
/************************* OUTPUT STUFF *************************/
void printSolution( Comb *combList, int length )
{
int i;
for( i = 0; i < length; i++ )
{
printf( "%d", combList[i].operand1 );
switch( combList[i].operation )
{
case NOOP: printf( " " ); break;
case ADD: printf( "+" ); break;
case SUB: printf( "-" ); break;
case MUL: printf( "*" ); break;
case DIV: printf( ":" ); break;
default: printf( " d%d ", combList[i].operation );
}
printf( "%d=%d", combList[i].operand2, calculate( &combList[i] ) );
if( i < length - 1 ) printf( "; " ); else printf( ".\n" );
}
printf( "\n" );
}
void printList( int *list, int length, int mask )
{
int i;
for( i = 0; i < length; i++ )
{
if( ( 1 << i ) & mask ) continue;
printf( "%d ", list[i] );
}
printf( "\n" );
}
/************************* ACTUAL SEARCH STUFF *************************/
void recSearch( int searchDepth, int usedMask )
/* searchDepth: current depth within the search.
usedMask: used to tell which elements of the work list
have been used.
*/
{
int currOp; /* current operation under consideration */
int newMask; /* or'ed with old mask to mark used numbers from work list */
int operand1; /* offset of operand 1 of combination within work list */
int operand2; /* offset of operand 2 of combination within work list */
if( stopSearch ) return; /* unroll recursion when solution is found */
nbNodes++; /* Statistics */
if( searchDepth == dmax )
{
/* check whether last number generated is nearer to t than best */
if( abs( workList[listLength + searchDepth - 1] - goal )
< abs( best - goal ) )
{
/* if so, save solution */
best = workList[listLength + searchDepth - 1];
bestDepth = searchDepth;
saveSolution( solution, combList, searchDepth );
if( best == goal )
{
printSolution( combList, searchDepth );
stopSearch = 1;
}
}
}
else
{
int working1, working2; /* hold values of numbers considered
for combination */
int temp; /* for swapping */
/* iterate over all four operators {+, -, *, /} */
for( currOp = ADD; currOp <= DIV; currOp++ )
{
for( operand1 = 0; operand1 < listLength + searchDepth;
operand1++ )
{
/* do not use already used numbers */
if( ( 1 << operand1 ) & usedMask ) continue;
for( operand2 = 0; operand2 < operand1; operand2++ )
{
if( ( 1 << operand2 ) & usedMask ) continue;
working1 = workList[operand1];
working2 = workList[operand2];
/* x * 1 = 1 * x = x; x / 1 = x */
if( ( currOp == MUL || currOp == DIV ) &&
( working1 == 1 || working2 == 1 ) ) continue;
/* could arise from combination a - a for some a */
if( working1 == 0 || working2 == 0 ) continue;
/* make dure operand2 divides operand1 */
if( currOp == DIV &&
( working1 % working2 ) ) continue;
/* make sure operand1 >= operand2 for subtraction and
division */
if( ( currOp == DIV || currOp == SUB ) &&
( working1 < working2 ) )
{
temp = working1;
working1 = working2;
working2 = temp;
}
/* mark operands used */
newMask = usedMask |
( 1 << operand1 ) |
( 1 << operand2 );
/* save current combination */
combList[searchDepth].operand1 =
working1;
combList[searchDepth].operand2 =
working2;
combList[searchDepth].operation = currOp;
workList[listLength + searchDepth] =
calculate( &combList[searchDepth] );
/* search deeper */
recSearch( searchDepth + 1, newMask );
}
}
}
}
}
void doSearch(void)
/* Preliminary search. Takes care of the special case where |S| = 1
and the only element of S is t.
*/
{
int i;
for( i = 0; i < listLength; i++ )
if( abs( workList[i] - goal ) < abs( best - goal ) )
{
best = workList[i];
}
if( best == goal )
{
printf( ".\n" );
return;
}
for( dmax = 1; dmax < listLength; dmax++ )
{
recSearch( 0, 0 );
if( stopSearch ) break;
}
/* If no exact solution was found */
if( stopSearch == 0 )
printSolution( solution, bestDepth );
}
int getInput(void)
{
int nums[16];
int i = 0;
int c;
nums[0] = 13;
nums[1] = 32;
nums[2] = 14;
nums[3] = 1412;
while( ( c = getchar() ) != '\n' && c != EOF )
{
ungetc( c, stdin );
fscanf( stdin, "%d", &nums[i] );
i++;
}
if( i == 0 ) i = 4;
listLength = i - 1;
goal = nums[listLength];
workList = newWorkList( 2 * listLength );
combList = newCombList( listLength );
solution = newCombList( listLength );
initWorkList( workList, nums, listLength );
initCombList( combList, listLength );
initCombList( solution, listLength );
return( listLength );
}
void search(void)
{
/* set up global variables for search */
stopSearch = 0;
nbNodes = 0;
doSearch();
}
int main( int argc, char *argv[] )
{
if( getInput() )
search();
return 0;
}