I have written the following Perl script to generate the logical constraints in smt2 format to solve a sudoku puzzle for a given input file. The input file is in this format:
5 3 * * 7 * * * *
6 * * 1 9 5 * * *
* 9 8 * * * * 6 *
8 * * * 6 * * * 3
4 * * 8 * 3 * * 1
7 * * * 2 * * * 6
* 6 * * * * 2 8 *
* * * 4 1 9 * * 5
* * * * 8 * * 7 9
The big ugly Perl script is:
#! /usr/local/bin/perl
# CSC 410 A2 Q2
# Sudoku
use strict;
use warnings;
# All of the indices in a sudoku array
my #row1 = qw(r1c1 r1c2 r1c3 r1c4 r1c5 r1c6 r1c7 r1c8 r1c9);
my #row2 = qw(r2c1 r2c2 r2c3 r2c4 r2c5 r2c6 r2c7 r2c8 r2c9);
my #row3 = qw(r3c1 r3c2 r3c3 r3c4 r3c5 r3c6 r3c7 r3c8 r3c9);
my #row4 = qw(r4c1 r4c2 r4c3 r4c4 r4c5 r4c6 r4c7 r4c8 r4c9);
my #row5 = qw(r5c1 r5c2 r5c3 r5c4 r5c5 r5c6 r5c7 r5c8 r5c9);
my #row6 = qw(r6c1 r6c2 r6c3 r6c4 r6c5 r6c6 r6c7 r6c8 r6c9);
my #row7 = qw(r7c1 r7c2 r7c3 r7c4 r7c5 r7c6 r7c7 r7c8 r7c9);
my #row8 = qw(r8c1 r8c2 r8c3 r8c4 r8c5 r8c6 r8c7 r8c8 r8c9);
my #row9 = qw(r9c1 r9c2 r9c3 r9c4 r9c5 r9c6 r9c7 r9c8 r9c9);
my #rows = (\#row1, \#row2, \#row3, \#row4, \#row5, \#row6, \#row7, \#row8, \#row9);
# All of the indices in a sudoku block
my #block1 = qw(r1c1 r1c2 r1c3 r2c1 r2c2 r2c3 r3c1 r3c2 r3c3);
my #block2 = qw(r1c4 r1c5 r1c6 r2c4 r2c5 r2c6 r3c4 r3c5 r3c6);
my #block3 = qw(r1c7 r1c8 r1c9 r2c7 r2c8 r2c9 r3c7 r3c8 r3c9);
my #block4 = qw(r4c1 r4c2 r4c3 r5c1 r5c2 r5c3 r6c1 r6c2 r6c3);
my #block5 = qw(r4c4 r4c5 r4c6 r5c4 r5c5 r5c6 r6c4 r6c5 r6c6);
my #block6 = qw(r4c7 r4c8 r4c9 r5c7 r5c8 r5c9 r6c7 r6c8 r6c9);
my #block7 = qw(r7c1 r7c2 r7c3 r8c1 r8c2 r8c3 r9c1 r9c2 r9c3);
my #block8 = qw(r7c4 r7c5 r7c6 r8c4 r8c5 r8c6 r9c4 r9c5 r9c6);
my #block9 = qw(r7c7 r7c8 r7c9 r8c7 r8c8 r8c9 r9c7 r9c8 r9c9);
my #blocks = (\#block1, \#block2, \#block3, \#block4, \#block5, \#block6, \#block7, \#block8, \#block9);
open (FORMULA, ">", "sudoku.smt2") or die $!;
my $var;
my $i;
my $r;
my $c;
print (FORMULA "; Declare integers constants.\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
print (FORMULA "(declare-const $rows[$r][$c] Int)\n");
}
}
print (FORMULA "\n");
print (FORMULA "; Assert for each variable r_ic_j that 1 <= r_ic_j <= 9\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
print (FORMULA "(assert (not (and (not ($rows[$r][$c] = 1)) (not ($rows[$r][$c] = 2)) (not ($rows[$r][$c] = 3)) (not ($rows[$r][$c] = 4)) (not ($rows[$r][$c] = 5)) (not ($rows[$r][$c] = 6)) (not ($rows[$r][$c] = 7)) (not ($rows[$r][$c] = 8)) (not ($rows[$r][$c] = 9)))))\n");
}
}
print (FORMULA "\n");
print (FORMULA "; Assert that each row and column contains a number only once.\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
$var = $rows[$r][$c];
for ($i = 0; $i < 9; $i++)
{
if ($var ne $rows[$r][$i])
{
print (FORMULA "(assert (not (= $var $rows[$r][$i])))\n");
}
}
for ($i = 0; $i < 9; $i++)
{
if ($var ne $rows[$i][$c])
{
print (FORMULA "(assert (not (= $var $rows[$i][$c])))\n");
}
}
}
}
print (FORMULA "\n");
print (FORMULA "; Assert that each number appears only once in each block.\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
$var = $blocks[$r][$c];
for ($i = 0; $i < 9; $i++)
{
if ($var ne $blocks[$r][$i])
{
print (FORMULA "(assert (not (= $var $blocks[$r][$i])))\n");
}
}
}
}
print (FORMULA "\n");
print (FORMULA "; Declare input constants\n");
open (INPUT, "<", $ARGV[0]) or die $!;
my #lines;
my $line_num = 0;
while (#lines = split(/ /, <INPUT>))
{
for ($i = 0; $i <= $#lines; $i++)
{
chomp($lines[$i]);
if ($lines[$i] ne "*")
{
print (FORMULA "(assert (= $rows[$line_num][$i] $lines[$i]))\n");
}
}
$line_num++;
}
print (FORMULA "\n");
print (FORMULA "(check-sat)\n");
print (FORMULA "(get-model)\n");
close (FORMULA);
exit;
The portion of the script that is causing the seg fault is:
print (FORMULA "; Assert for each variable r_ic_j that 1 <= r_ic_j <= 9\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
print (FORMULA "(assert (not (and (not ($rows[$r][$c] = 1)) (not ($rows[$r][$c] = 2)) (not ($rows[$r][$c] = 3)) (not ($rows[$r][$c] = 4)) (not ($rows[$r][$c] = 5)) (not ($rows[$r][$c] = 6)) (not ($rows[$r][$c] = 7)) (not ($rows[$r][$c] = 8)) (not ($rows[$r][$c] = 9)))))\n");
}
}
But this fragment just prints something like this:
(assert (not (and (not (x = 1)) (not (x = 2)) (not (x = 3)) ... (not (x = 9)))))
which should be logically equivalent to:
(assert (or (= x 1) (= x 2) (= x 3) ... (= x 9)))
Any advice is appreciated.
Regards.
The expression (x = 1) is not valid in SMT 2.0. It should be (= x 1). As you described in the comment above, after this change is made, the script works as expected.
Related
I'm trying to understand elliptic curve cryptography by coding the algorithm in perl. But getting detailed information about implementing ECC is quite difficult.
So I gathered all informations together and started coding.
At first I searched for informations about a simple curve and I found it here: http://www.hjp.at/doc/rfc/rfc5639.html. Several curves from 160 upto 512 bits. Of course I started with the smallest one.
EDIT: Made a lot of mistakes, mostly due to the lack of information. But now this example works.
This is my perl script:
use bignum;
use Math::BigInt::Random;
my $p = hex "E95E4A5F737059DC60DFC7AD95B3D8139515620F";
my $a = hex "340E7BE2A280EB74E2BE61BADA745D97E8F7C300";
my $b = hex "1E589A8595423412134FAA2DBDEC95C8D8675E58";
my $x = hex "BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3";
my $y = hex "1667CB477A1A8EC338F94741669C976316DA6321";
my $q = hex "E95E4A5F737059DC60DF5991D45029409E60FC09";
my $bitLength = 160;
sub point_doubling {
my ($px, $py) = #_;
return $px, $py if $px == 0 && $py == 0;
my $slope_numerator = 3 * $px * $px + $a;
my $slope_denominator = 2 * $py;
my $slope_denominator_inverse_modulo = $slope_denominator->bmodinv($p);
my $slope_inverse_product = $slope_numerator * $slope_denominator_inverse_modulo;
my $slope = $slope_inverse_product % $p;
my $rx = (($slope * $slope - 2 * $px)) % $p;
my $ry = (($slope * ($px - $rx) - $py)) % $p;
return ($rx, $ry);
}
sub point_addition {
my ($px, $py, $qx, $qy) = #_;
return point_doubling($px, $py) if $px == $qx && $py == $qy;
return $px, $py if $qx == 0 && $qy == 0;
return $qx, $qy if $px == 0 && $py == 0;
my $slope_numerator = $qy - $py;
my $slope_denominator = $qx - $px;
my $slope_denominator_inverse_modulo = $slope_denominator->bmodinv($p);
my $slope_inverse_product = $slope_numerator * $slope_denominator_inverse_modulo;
my $slope = $slope_inverse_product % $p;
my $rx = (($slope * $slope - $px - $qx)) % $p;
my $ry = (($slope * ($px - $rx) - $py)) % $p;
return ($rx, $ry);
}
sub scalar_product {
my ($k, $nx, $ny) = #_;
my ($qx, $qy) = (0, 0);
my $bit = 1;
for my $bitCounter (1..$bitLength) {
if ($k & $bit) {
($qx, $qy) = point_addition($nx, $ny, $qx, $qy);
}
($nx, $ny) = point_doubling($nx, $ny);
$bit <<= 1;
}
return ($qx, $qy);
}
sub check_point_on_curve {
my ($qx, $qy) = #_;
return $qy * $qy % $p == ($qx * $qx * $qx + $a * $qx + $b) % $p ||
$qy == 0 && $qx == 0;
}
print "Test if G is in curve:\n";
if (check_point_on_curve($x, $y)) {
print "G is a point on the curve\n";
} else {
die "ERROR: G is NOT a point on the curve\n";
}
# Creating the private key.
# 1.) A random integer dA with 0 < dA < q
my $dA = random_bigint(max => $q);
print "Private key chosen as $dA\n";
# 2.) Calculate the public key QA
my ($qax, $qay) = scalar_product($dA, $x, $y);
print "Public key calculated to ($qax, $qay)\n";
if (check_point_on_curve($qax, $qay)) {
print "Public key is a point on the curve\n";
} else {
die "ERROR: Public key is NOT a point on the curve\n";
}
The result looks likle this:
Test if G is in curve:
G is a point on the curve
Private key chosen as 14682691932678591389241030591930855095113892
Public key calculated to (1114548639616364108201749083649167655259366359581, 418437095262665064210367316915287142897094980157)
Public key is a point on the curve
Thanks for all your help.
Currently I'm learning Perl and gnuplot. I would like to know how to count certain value based on the next value. For example I have a text file consist of:
#ID(X) Y
1 1
3 9
5 11
The output should show the value of the unknown ID as well. So, the output should show:
#ID(X) Y
1 1
2 5
3 9
4 10
5 11
The Y of ID#2 is based on the following:
((2-3)/(1-3))*1 + ((2-1)/(3-1))*9 which is linear algebra
Y2=((X2-X3)/(X1-X3))*Y1 + ((X2-X1)/(X3-X1)) * Y3
Same goes to ID#5
Currently I have this code,
#! /usr/bin/perl -w
use strict;
my $prev_id = 0;
my $prev_val = 0;
my $next_id;
my $next_val;
while (<>)
{
my ($id, $val) = split;
for (my $i = $prev_id + 1; $i < $next_id; $i++)
{
$val = (($id - $next_id) / ($prev_id - $next_id)) * $prev_val + (($id - $prev_id) / ($next_id - $prev_id)) * $next_val;
printf ("%d %s\n", $i, $val);
}
printf ("%d %s\n", $id, $val);
($prev_val, $prev_id) = ($val, $id);
($next_val, $next_id) = ($prev_val, $prev_id);
}
Your formula seems more complicated than I would expect, given that you are always dealing with integer spacings of 1.
You did not say whether you want to fill gaps for multiple consecutive missing values, but let's assume you want to.
What you do is read in the first line, and say that's the current one and you output it. Now you read the next line, and if its ID is not the expected one, you fill the gaps with simple linear interpolation...
Pseudocode
(currID, currY) = readline()
outputvals( currID, currY )
while lines remain do
(nextID, nextY) = readline()
gap = nextID - currID
for i = 1 to gap
id = currID + i
y = currY + (nextY - currY) * i / gap
outputvals( id, y )
end
(currID, currY) = (nextID, nextY)
end
Sorry for the non-Perl code. It's just that I haven't been using Perl for ages, and can't remember half of the syntax. =) The concepts here are pretty easy to translate into code though.
Using an array may be the way to go. This will also make your data available for further manipulation.
** Caveat: will not work for multiple consecutive missing values of y; see #paddy's answer.
#!/usr/bin/perl
use strict;
use warnings;
my #coordinates;
while (<DATA>) {
my ($x, $y) = split;
$coordinates[$x] = $y;
}
# note that the for loop starts on index 1 here ...
for my $x (1 .. $#coordinates) {
if (! $coordinates[$x]) {
$coordinates[$x] = (($x - ($x + 1)) / (($x - 1) - ($x + 1)))
* $coordinates[$x - 1]
+ (($x - ($x - 1)) / (($x + 1) - ($x - 1)))
* $coordinates[$x + 1];
}
print "$x - $coordinates[$x]\n";
}
__DATA__
1 1
3 9
5 11
You indicated your problem is getting the next value. The key isn't to look ahead, it's to look behind.
my $prev = get first value;
my ($prev_a, $prev_b) = parse($prev);
my $this = get second value;
my ($this_a, $this_b) = parse($this);
while ($next = get next value) {
my ($next_a, $next_b) = parse($next);
...
$prev = $this; $prev_a = $this_a; $prev_b = $this_b;
$this = $next; $this_a = $next_a; $this_b = $next_b;
}
#! /usr/bin/perl -w
use strict;
my #in = (1,9,11);
my #out;
for (my $i = 0; $i<$#in; $i++) {
my $j = $i*2;
my $X1 = $i;
my $X2 = $i+1;
my $X3 = $i+2;
my $Y1 = $in[$i];
my $Y3 = $in[$i+1];
my $Y2 = $Y1*(($X2-$X3)/($X1-$X3))
+ $Y3*(($X2-$X1)/($X3-$X1));
$out[$j] = $in[$i];
$out[$j+1] = $Y2;
}
$out[$#in*2] = $in[$#in];
print (join " ",#out);
I am attempting to add non-interlaced GIF images other than 8-bit to a PDF document without having to fully decode the bitstream using PDF::Create for Perl.
The LZWDecode algorithm that is part of the PDF standard requires all images to have a minimum LZW code size of 8-bits, and PDF::Create is hard-coded to only embed 8-bit images.
So far, I have adapted the image loader from PDF::Create to read a 5-bit image and to fully decode the LZW stream. I am then able to use the encoder algorithm from PDF::Create to re-pack the image as 8-bit.
What I'd like to do is to eliminate the memory-intensive decode/encode step. This thread suggests that this is possible by "widening or shifting bits" to make LZW codes the proper length for LZWDecode.
I contacted the thread author and he provided some additional details, in particular that codes for color indices remain the same but are padded with zeros (e.g., [10000] becomes [000010000]), that <Clear> and <End> codes are changed to <256> and <257> respectively, and that all other codes are offset by 256 - original <Clear> code.
However, he was unable to elaborate further due to restrictions by his employer. In particular, I am uncertain how to handle a code when its modified value exceeds <4095> (the maximum index for the LZW code table). I am also unsure how to re-pack the revised codes into a bitstream.
The algorithms I am currently using are below.
# Read 5-bit data stream
sub ReadData5 {
my $data = shift;
my $c_size = 6; # minimium LZW code size
my $t_size = 33; # initial code table size
my ($i_buff,$i_bits) = (0,0); # input buffer
my ($o_buff,$o_bits) = (0,0); # output buffer
my $stream = ''; # bitstream
my $pos = 0;
SUB_BLOCK: while (1){
my $s = substr($data, $pos++, 1);
# get sub-block size
my $n_bytes = unpack('C', $s) or last SUB_BLOCK;
my $c_mask = (1 << $c_size) - 1;
BYTES: while (1){
# read c_size bits
while ($i_bits < $c_size){
# end of sub-block
!$n_bytes-- and next SUB_BLOCK;
$s = substr($data, $pos++, 1);
my $c = unpack('C', $s);
$i_buff |= $c << $i_bits;
$i_bits += 8;
}
# write c_size bits
my $code = $i_buff & $c_mask;
my $w_bits = $c_size;
$i_buff >>= $c_size;
$i_bits -= $c_size;
$t_size++;
if ($o_bits > 0){
$o_buff |= $code >> ($c_size - 8 + $o_bits);
$w_bits -= 8 - $o_bits;
$stream .= pack('C', $o_buff & 0xFF);
}
if ($w_bits >= 8){
$w_bits -= 8;
$stream .= pack('C', ($code >> $w_bits) & 0xFF);
}
if (($o_bits = $w_bits) > 0){
$o_buff = $code << (8 - $o_bits);
}
# clear code
if ($code == 32){
$c_size = 6;
$t_size = 33;
$c_mask = (1 << $c_size) - 1;
}
# end code
if ($code == 33){
$stream .= pack('C', $o_buff & 0xFF);
last SUB_BLOCK;
}
if ($t_size == (1 << $c_size)){
if (++$c_size > 12){
$c_size--;
} else {
$c_mask = (1 << $c_size) - 1;
}
}
}
}
# Pad with zeros to byte boundary
$stream .= '0' x (8 - length($stream) % 8);
return $stream;
}
#---------------------------------------------------------------------------
# Decode 5-bit data stream
sub UnLZW5 {
my $data = shift;
my $c_size = 6; # minimium LZW code size
my $t_size = 33; # initial code table size
my ($i_buff,$i_bits) = (0,0); # input buffer
my $stream = ''; # bitstream
my $pos = 0;
# initialize code table
my #table = map { chr($_) } 0..$t_size-2;
$table[32] = '';
my $prefix = '';
my $suffix = '';
# get first code word
while ($i_bits < $c_size){
my $d = unpack('C', substr($data, $pos++, 1));
$i_buff = ($i_buff << 8) + $d;
$i_bits += 8;
}
my $c2 = $i_buff >> ($i_bits - $c_size);
$i_bits -= $c_size;
my $c_mask = (1 << $i_bits) - 1;
$i_buff &= $c_mask;
# get remaining code words
DECOMPRESS: while ($pos < length($data)){
my $c1 = $c2;
while ($i_bits < $c_size){
my $d = unpack('C', substr($data, $pos++, 1));
$i_buff = ($i_buff << 8) + $d;
$i_bits += 8;
}
$c2 = $i_buff >> ($i_bits - $c_size);
$i_bits -= $c_size;
$c_mask = (1 << $i_bits) - 1;
$i_buff &= $c_mask;
# clear code
if ($c2 == 32){
$stream .= $table[$c1];
$#table = 32;
$c_size = 6;
$t_size = 33;
next DECOMPRESS;
}
# end code
if ($c2 == 33){
$stream .= $table[$c1];
last DECOMPRESS;
}
# get prefix and suffix
$prefix = $table[$c1] if $c1 < $t_size;
$suffix = $c2 < $t_size ? substr($table[$c2], 0, 1) : substr($prefix, 0, 1);
# write prefix
$stream .= $prefix;
# write multiple-character sequence
$table[$t_size++] = $prefix . $suffix;
# increase code size
if ($t_size == 2 ** $c_size){
if (++$c_size > 12){
$c_size--;
}
}
}
return $stream;
}
Doing one at a time is slow. Doing them all at once takes too much memory for you. Do them a chunk at a time.
my $BUFFER_SIZE = 5 * 50_000; # Must be a multiple of 5.
my $in_bytes = ...;
my $out_bytes = '';
while (my ($bytes) = $in_bytes =~ s/^(.{1,$BUFFER_SIZE})//s) {
# Unpack from 5 bit fields.
my #vals = map { pack('B*', "000$_") } unpack('B*', $bytes) =~ /(.{5})/g;
# Transform #vals into 8 bit values here.
# Pack to 8 bit fields.
$out_bytes .= pack('C*', #vals);
}
Since you're not transforming the values at all (just how they are stored), that simplifies to:
my $BUFFER_SIZE = 5 * 50_000; # Must be a multiple of 40.
my $in_bytes = ...;
my $out_bytes = '';
while (my ($bytes) = $in_bytes =~ s/^(.{1,$BUFFER_SIZE})//s) {
# Unpack from 5 bit fields.
my $bits = unpack('B*', $bytes);
$bits =~ s/(.{5})/000$1/g;
$out_bytes .= pack('B*', $bits);
}
You didn't say what to do with the extra bits. I simply ignored them.
Alternative approach with no bit string creation:
my $in_bytes = ...;
my $out_bytes = '';
while (my ($bytes) = $in_bytes =~ s/^(.{1,5})//s) {
my #bytes = map ord, split //, $bytes;
# 00000111 11222223 33334444 45555566 66677777
$out_bytes .= chr( (($bytes[0] >> 3) & 0x1F));
last if #bytes == 1;
$out_bytes .= chr((($bytes[0] << 2) & 0x1C) | (($bytes[1] >> 6) & 0x03));
$out_bytes .= chr( (($bytes[1] >> 1) & 0x1F));
last if #bytes == 2;
$out_bytes .= chr((($bytes[1] << 4) & 0x10) | (($bytes[2] >> 4) & 0x0F));
last if #bytes == 3;
$out_bytes .= chr((($bytes[2] << 1) & 0x1E) | (($bytes[3] >> 7) & 0x01));
$out_bytes .= chr( (($bytes[3] >> 2) & 0x1F));
last if #bytes == 4;
$out_bytes .= chr((($bytes[3] << 3) & 0x18) | (($bytes[4] >> 5) & 0x07));
$out_bytes .= chr( ( $bytes[4] & 0x1F));
}
The advantage of the above solution is that it's particularly efficient in C.
STRLEN in_len;
const char* in = SvPVbyte(sv, in_len);
STRLEN out_len = (in_len * 8 / 5) * 8;
char* out = (char*)malloc(out_len);
char* out_cur = out;
char* in_end = in + in_len;
while (in != in_end) {
*(out_cur++) = ((*in >> 3) & 0x1F));
if (++in == in_end) break;
*(out_cur++) = ((in[-1] << 2) & 0x1C) | ((*in >> 6) & 0x03));
*(out_cur++) = ((*in >> 1) & 0x1F));
if (++in == in_end) break;
*(out_cur++) = ((in[-1] << 4) & 0x10) | ((*in >> 4) & 0x0F));
if (++in == in_end) break;
*(out_cur++) = ((in[-1] << 1) & 0x1E) | ((*in >> 7) & 0x01));
*(out_cur++) = ((*in >> 2) & 0x1F));
if (++in == in_end) break;
*(out_cur++) = ((in[-1] << 3) & 0x18) | ((*in >> 5) & 0x07));
*(out_cur++) = ( *in & 0x1F));
}
return newSVpvn(out, out_len);
I am running the following in Perl v5.12.3 on Mac OS X v10.7.2 (Lion):
#!/usr/local/bin/perl
use strict;
use warnings;
use DBI;
my $db = DBI->connect("dbi:SQLite:testdrive.db") or die "Cannot connect: $DBI::errstr";
my #times = ("13:00","14:30","16:00","17:30","19:00","20:30","22:00");
my $counter = 1;
for (my $d = 1; $d < 12; $d++) {
for (my $t = 0; $t < 7; $t++) {
# Weekend days have seven slots, weekdays
# have only four (barring second friday)
if (($d+4) % 7 < 2 || ($t > 3)) {
$db->do("INSERT INTO tbl_timeslot VALUES ($counter, '$times[$t]', $d);");
$counter++;
# Add 4:00 slot for second Friday
} elsif (($d = 9) && ($t = 3)) {
$db->do("INSERT INTO tbl_timeslot VALUES ($counter, '$times[$t]', $d);");
$counter++;
}
}
}
$db->disconnect;
I get a "Found = in conditional, should be == at addtimes.pl line 16" warning, but there's no equal sign on that line. Also, the loop seems to start at $d == 9. What am I missing?
Line 16:
if (($d+4) % 7 < 2 || ($t > 3)) {
The problem is in your elsif
} elsif (($d = 9) && ($t = 3)) {
^-----------^--------- should be ==
Because the if statement started on line 16, and the elsif is part of that statement, that's where the error got reported from. This is an unfortunate limitation of the Perl compiler.
On an unrelated note, it's much nicer to avoid C-style loops when you can:
for my $d ( 1 .. 11 ) {
...
for my $t ( 0 .. 6 ) {
...
}
}
Isn't that prettier? :)
} elsif (($d = 9) && ($t = 3)) {
This line will assign 9 to $d and 3 to $t. As the warning says, you probably want this instead:
} elsif (($d == 9) && ($t == 3)) {
For those not familiar with the game. You're given 8 numbers and you have to reach the target by using +, -, / and *.
So if the target is 254 and your game numbers are 2, 50, 5, 2, 1, you would answer the question correctly by saying 5 * 50 = 250. Then 2+2 is four. Add that on aswell to get 254.
Some videos of the game are here:
Video 1
video 2
Basically I brute force the game using by generating all perms of all sizes for the numbers and all perms of the symbols and use a basic inflix calculator to calculate the solution.
However it contains a flaw because all the solutions are solved as following: ((((1+1)*2)*3)*4). It doesn't permutate the brackets and it's causing my a headache.
Therefore I cannot solve every equation. For example, given
A target of 16 and the numbers 1,1,1,1,1,1,1,1 it fails when it should do (1+1+1+1)*(1+1+1+1)=16.
I'd love it in someone could help finish this...in any language.
This is what I've written so far:
#!/usr/bin/env perl
use strict;
use warnings;
use Algorithm::Permute;
# GAME PARAMETERS TO FILL IN
my $target = 751;
my #numbers = ( '2', '4', '7', '9', '1', '6', '50', '25' );
my $num_numbers = scalar(#numbers);
my #symbols = ();
foreach my $n (#numbers) {
push(#symbols, ('+', '-', '/', '*'));
}
my $num_symbols = scalar(#symbols);
print "Symbol table: " . join(", ", #symbols);
my $lst = [];
my $symb_lst = [];
my $perms = '';
my #perm = ();
my $symb_perms = '';
my #symb_perm;
my $print_mark = 0;
my $progress = 0;
my $total_perms = 0;
my #closest_numbers;
my #closest_symb;
my $distance = 999999;
sub calculate {
my #oprms = #{ $_[0] };
my #ooperators = #{ $_[1] };
my #prms = #oprms;
my #operators = #ooperators;
#print "PERMS: " . join(", ", #prms) . ", OPERATORS: " . join(", ", #operators);
my $total = pop(#prms);
foreach my $operator (#operators) {
my $x = pop(#prms);
if ($operator eq '+') {
$total += $x;
}
if ($operator eq '-') {
$total -= $x;
}
if ($operator eq '*') {
$total *= $x;
}
if ($operator eq '/') {
$total /= $x;
}
}
#print "Total: $total\n";
if ($total == $target) {
#print "ABLE TO ACCURATELY SOLVE WITH THIS ALGORITHM:\n";
#print "PERMS: " . join(", ", #oprms) . ", OPERATORS: " . join(", ", #ooperators) . ", TOTAL=$total\n";
sum_print(\#oprms, \#ooperators, $total, 0);
exit(0);
}
my $own_distance = ($target - $total);
if ($own_distance < 0) {
$own_distance *= -1;
}
if ($own_distance < $distance) {
#print "found a new solution - only $own_distance from target $target\n";
#print "PERMS: " . join(", ", #oprms) . ", OPERATORS: " . join(", ", #ooperators) . ", TOTAL=$total\n";
sum_print(\#oprms, \#ooperators, $total, $own_distance);
#closest_numbers = #oprms;
#closest_symb = #ooperators;
$distance = $own_distance;
}
$progress++;
if (($progress % $print_mark) == 0) {
print "Tested $progress permutations. " . (($progress / $total_perms) * 100) . "%\n";
}
}
sub factorial {
my $f = shift;
$f == 0 ? 1 : $f*factorial($f-1);
}
sub sum_print {
my #prms = #{ $_[0] };
my #operators = #{ $_[1] };
my $total = $_[2];
my $distance = $_[3];
my $tmp = '';
my $op_len = scalar(#operators);
print "BEST SOLUTION SO FAR: ";
for (my $x = 0; $x < $op_len; $x++) {
print "(";
}
$tmp = pop(#prms);
print "$tmp";
foreach my $operator (#operators) {
$tmp = pop(#prms);
print " $operator $tmp)";
}
if ($distance == 0) {
print " = $total\n";
}
else {
print " = $total (distance from target $target is $distance)\n";
}
}
# look for straight match
foreach my $number (#numbers) {
if ($number == $target) {
print "matched!\n";
}
}
for (my $x = 1; $x < (($num_numbers*2)-1); $x++) {
$total_perms += factorial($x);
}
print "Total number of permutations: $total_perms\n";
$print_mark = $total_perms / 100;
if ($print_mark == 0) {
$print_mark = $total_perms;
}
for (my $num_size=2; $num_size <= $num_numbers; $num_size++) {
$lst = \#numbers;
$perms = new Algorithm::Permute($lst, $num_size);
print "Perms of size: $num_size.\n";
# print matching symb permutations
$symb_lst = \#symbols;
$symb_perms = new Algorithm::Permute($symb_lst, $num_size-1);
while (#perm = $perms->next) {
while (#symb_perm = $symb_perms->next) {
calculate(\#perm, \#symb_perm);
}
$symb_perms = new Algorithm::Permute($symb_lst, $num_size-1);
}
}
print "exhausted solutions";
print "CLOSEST I CAN GET: $distance\n";
sum_print(\#closest_numbers, \#closest_symb, $target-$distance, $distance);
exit(0);
Here is the example output:
[15:53: /mnt/mydocuments/git_working_dir/countdown_solver$] perl countdown_solver.pl
Symbol table: +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *Total number of permutations: 93928268313
Perms of size: 2.
BEST SOLUTION SO FAR: (2 + 4) = 6 (distance from target 751 is 745)
BEST SOLUTION SO FAR: (2 * 4) = 8 (distance from target 751 is 743)
BEST SOLUTION SO FAR: (4 + 7) = 11 (distance from target 751 is 740)
BEST SOLUTION SO FAR: (4 * 7) = 28 (distance from target 751 is 723)
BEST SOLUTION SO FAR: (4 * 9) = 36 (distance from target 751 is 715)
BEST SOLUTION SO FAR: (7 * 9) = 63 (distance from target 751 is 688)
BEST SOLUTION SO FAR: (4 * 50) = 200 (distance from target 751 is 551)
BEST SOLUTION SO FAR: (7 * 50) = 350 (distance from target 751 is 401)
BEST SOLUTION SO FAR: (9 * 50) = 450 (distance from target 751 is 301)
Perms of size: 3.
BEST SOLUTION SO FAR: ((4 + 7) * 50) = 550 (distance from target 751 is 201)
BEST SOLUTION SO FAR: ((2 * 7) * 50) = 700 (distance from target 751 is 51)
BEST SOLUTION SO FAR: ((7 + 9) * 50) = 800 (distance from target 751 is 49)
BEST SOLUTION SO FAR: ((9 + 6) * 50) = 750 (distance from target 751 is 1)
Perms of size: 4.
BEST SOLUTION SO FAR: (((9 + 6) * 50) + 1) = 751
Here is Java applet (source) and Javascript version.
The suggestion to use reverse polish notation is excellent.
If you have N=5 numbers, the template is
{num} {num} {ops} {num} {ops} {num} {ops} {num} {ops}
There can be zero to N ops in any spot, although the total number will be N-1. You just have to try different placements of numbers and ops.
The (((1+1)+1)+1)*(((1+1)+1)+1)=16 solution will be found when you try
1 1 + 1 + 1 + 1 1 + 1 + 1 + *
Update: Maybe not so good, since finding the above could take 433,701,273,600 tries. The number was obtained using the following:
use strict;
use warnings;
{
my %cache = ( 0 => 1 );
sub fact { my ($n) = #_; $cache{$n} ||= fact($n-1) * $n }
}
{
my %cache;
sub C {
my ($n,$r) = #_;
return $cache{"$n,$r"} ||= do {
my $i = $n;
my $j = $n-$r;
my $c = 1;
$c *= $i--/$j-- while $j;
$c
};
}
}
my #nums = (1,1,1,1,1,1,1,1);
my $Nn = 0+#nums; # Number of numbers.
my $No = $Nn-1; # Number of operators.
my $max_tries = do {
my $num_orderings = fact($Nn);
{
my %counts;
++$counts{$_} for #nums;
$num_orderings /= fact($_) for values(%counts);
}
my $op_orderings = 4 ** $No;
my $op_placements = 1;
$op_placements *= C($No, $_) for 1..$No-1;
$num_orderings * $op_orderings * $op_placements
};
printf "At most %.f tries needed\n", $max_tries;