Branch and bound using Perl - perl

I have a problem, I cannot find an answer to. I am using Perl. My input is a symmetric cost-matrix, kind of like the TSP.
I want to know all solutions that lie beneath my boundary, which is 10.
This is my matrix:
- B E G I K L P S
B - 10 10 2 10 10 10 10
E 10 - 2 10 10 10 1 10
G 10 2 - 10 2 3 3 3
I 2 10 10 - 4 10 10 2
K 10 10 2 4 - 10 10 3
L 10 10 3 10 10 - 2 2
P 10 1 3 10 10 2 - 10
S 10 10 3 2 3 2 10 -
Does anybody know how to implement the branch and bound algorithm to solve this? For now, I did replace every 10 in the matrix with "-".
What I did so far:
#verwbez = ( ["-", B, E, G, I, K, L, P, S],
[B,"-", 10, 10, 2, 10, 10, 10, 10],
[E, 10, "-", 2, 10, 10, 10, 1, 10],
[G, 10, 2, "-", 10, 2, 3, 3, 3],
[I, 2, 10, 10, "-", 4, 10, 10, 2],
[K, 10, 10, 2, 4, "-", 10, 10, 3],
[L, 10, 10, 3, 10, 10, "-", 2, 2],
[P, 10, 1, 3, 10, 10, 2, "-", 10],
[S, 10, 10, 3, 2, 3, 2, 10, "-"]);
for ($i=0;$i<=$#verwbez;$i++) {
for ($j=0; $j<=$#{$verwbez[$i]};$j++) {
while ($verwbez[$i][$j] >=7) {
$verwbez[$i][$j] = "-";
}
}
}
Basically just altering the matrix, every 10 is replaced with a "-". Now I want to find all solutions that are beneath 10 and contain 4 districts where always two cities are linked together. But unfortunately, I do not know how to proceed/start...

You're unlikely to get someone to implement the Branch and Bound algorithm for you. However, the following stackoverflow post, TSP - branch and bound, has some links to some helpful resources:
Optimal Solution for TSP using Branch and Bound
B&B Implementations for the TSP -
Part 1: A solution with nodes containing partial tours with
constraints
B&B Implementations for the TSP - Part 2: Single threaded solution with many inexpensive nodes
Since you appear new to perl, we can give you some quick tips
Always include use strict; and use warnings at the top of each and every perl script
Use the range operator .. when creating an incrementing for loop.
Your while loop should actually be an if statement.
For increased style, consider using qw() when initializing a mixed word/number array, especially since it will allow you to easily align a multidimensional array's elements
Your first goal for a project like this should be to create a method to output your multidimensional array in a readable format, so you can observe and verify the changes that you're making.
All of that gives the following changes:
use strict;
use warnings;
my #verwbez = (
[qw(- B E G I K L P S )],
[qw(B - 10 10 2 10 10 10 10)],
[qw(E 10 - 2 10 10 10 1 10)],
[qw(G 10 2 - 10 2 3 3 3 )],
[qw(I 2 10 10 - 4 10 10 2 )],
[qw(K 10 10 2 4 - 10 10 3 )],
[qw(L 10 10 3 10 10 - 2 2 )],
[qw(P 10 1 3 10 10 2 - 10)],
[qw(S 10 10 3 2 3 2 10 - )],
);
for my $i (0 .. $#verwbez) {
for my $j (0 .. $#{$verwbez[$i]}) {
if ($verwbez[$i][$j] =~ /\d/ && $verwbez[$i][$j] >= 7) {
$verwbez[$i][$j] = ".";
}
}
}
for (#verwbez) {
for (#$_) {
printf "%2s ", $_;
}
print "\n";
}
Outputs:
- B E G I K L P S
B - . . 2 . . . .
E . - 2 . . . 1 .
G . 2 - . 2 3 3 3
I 2 . . - 4 . . 2
K . . 2 4 - . . 3
L . . 3 . . - 2 2
P . 1 3 . . 2 - .
S . . 3 2 3 2 . -
Note that B has only 1 city it's near to. So if the goal was solving the TSP, then there isn't a trivial solution. However, given there are only 8 cities and (n-1)! circular permutations. That gives us just 5,040 permutations, so using brute force would totally work for finding a lowest cost solution.
use strict;
use warnings;
use Algorithm::Combinatorics qw(circular_permutations);
my #verwbez = ( ... already defined ... );
# Create a cost between two cities hash:
my %cost;
for my $i (1..$#verwbez) {
for my $j (1..$#{$verwbez[$i]}) {
$cost{ $verwbez[$i][0] }{ $verwbez[0][$j] } = $verwbez[$i][$j] if $i != $j;
}
}
# Determine all Routes and their cost (sorted)
my #cities = keys %cost;
my #perms = circular_permutations(\#cities);
my #cost_with_perm = sort {$a->[0] <=> $b->[0]} map {
my $perm = $_;
my $prev = $perm->[-1];
my $cost = 0;
for (#$perm) {
$cost += $cost{$_}{$prev};
$prev = $_
}
[$cost, $perm]
} #perms;
# Print out lowest cost routes:
print "Lowest cost is: " . $cost_with_perm[0][0] . "\n";
for (#cost_with_perm) {
last if $_->[0] > $cost_with_perm[0][0];
print join(' ', #{$_->[1]}), "\n";
}
It ends up there are only 2 lowest cost solutions to this setup, and they're mirror images of each other, which makes sense since we didn't filter by direction in our circular permutations. Am intentionally not stating what they are here.

Related

In Raku, how does one calculate the sum of positive divisors from a prime factorization?

In Raku, given a list of pairs (2 => 3, 3 => 2, 5 => 1, 7 => 4) ( representing the prime factorization of n = 2 3 · 3 2 · 5 1 · 7 4 ), how does construct a Raku expression for σ(n) = ( 2 0 + 2 1 + 2 2 + 2 3 ) · ( 3 0 + 3 1 + 3 2 ) · ( 5 0 + 5 1 ) · ( 7 0 + 7 1 + 7 2 + 7 3 + 7 4 ) ?
sub MAIN()
{
my $pairList = (2 => 3, 3 => 2, 5 => 1, 7 => 4) ;
say '$pairList' ;
say $pairList ;
say $pairList.WHAT ;
# Goal:
# from $pairList,
# the product (1 + 2 + 4 + 8) * (1 + 3 + 9) * (1 + 5) * (1 + 7 + 49 + 343 + 2401)
# = sigma ( 2^3 * 3^2 * 5^1 * 7^4 )
} # end sub MAIN
Update 1
Based upon the answer of #raiph, the following program breaks the overall process into stages for the newcomer to Raku (such as me) …
sub MAIN()
{
my $pairList = (2 => 3, 3 => 2, 5 => 1, 7 => 4) ;
say '$pairList' ;
say $pairList ;
say $pairList.WHAT ;
# Goal:
# from $pairList,
# the product (1 + 2 + 4 + 8) * (1 + 3 + 9) * (1 + 5) * (1 + 7 + 49 + 343 + 2401)
# the product (15) * (13) * (6) * (2801)
# sigma ( 2^3 * 3^2 * 5^1 * 7^4 )
# 3277170
# Stage 1 : ((1 2 4 8) (1 3 9) (1 5) (1 7 49 343 2401))
my $stage1 = $pairList.map: { (.key ** (my $++)) xx (.value + 1) } ;
say '$stage1 : lists of powers' ;
say $stage1 ;
say $stage1.WHAT ;
# Stage 2 : ((1 + 2 + 4 + 8) (1 + 3 + 9) (1 + 5) (1 + 7 + 49 + 343 + 2401))
my $stage2 = $stage1.map: { sum $_ } ;
say '$stage2 : sum each list' ;
say $stage2 ;
say $stage2.WHAT ;
# Stage 3 : (1 + 2 + 4 + 8) * (1 + 3 + 9) * (1 + 5) * (1 + 7 + 49 + 343 + 2401)
my $stage3 = $stage2.reduce( &infix:<*> ) ;
say '$stage3 : product of list elements' ;
say $stage3 ;
say $stage3.WHAT ;
} # end sub MAIN
A related post appears on Mathematics Stack Exchange.
Update 2
My original motivation had been to calculate aliquot sum s(n) = σ(n) - n. I found that prime factorization of each n is not necessary and seems inefficient. Raku and C++ programs calculating s(n) for n = 0 … 10 6 follow …
Raku
sub MAIN()
{
constant $limit = 1_000_000 ;
my #s of Int = ( 1 xx ($limit + 1) ) ;
#s[0] = 0 ;
#s[1] = 0 ;
loop ( my $column = 2; $column <= ($limit + 1) div 2; $column++ )
{
loop ( my $row = (2 * $column); $row <= $limit; $row += $column )
{
#s[$row] += $column ;
} # end loop $row
} # end loop $column
say "s(", $limit, ") = ", #s[$limit] ; # s(1000000) = 1480437
} # end sub MAIN
C++
(Observed to execute significantly faster than Raku)
#include <iostream>
#include <vector>
using namespace std ;
int main ( void )
{
const int LIMIT = 1000000 ;
vector<int> s ( (LIMIT + 1), 1 ) ;
s[0] = 0 ;
s[1] = 0 ;
for ( int col = 2 ; col <= (LIMIT + 1) / 2 ; col++ )
for ( int row = (2 * col) ; row <= LIMIT ; row += col )
s[row] += col ;
cout << "s(" << LIMIT << ") = " << s[LIMIT] << endl ; // s(1000000) = 1480437
} // end function main
There'll be bazillions of ways. I've ignored algorithmic efficiency. The first thing I wrote:
say [*] (2 => 3, 3 => 2, 5 => 1, 7 => 4) .map: { sum .key ** my $++ xx .value + 1 }
displays:
3277170
Explanation
1 say
2 [*] # `[op]` is a reduction. `[*] 6, 8, 9` is `432`.
3 (2 => 3, 3 => 2, 5 => 1, 7 => 4)
4 .map:
5 {
6 sum
7 .key # `.key` of `2 => 3` is `2`.
8 **
9 my # `my` resets `$` for each call of enclosing `{...}`
10 $++ # `$++` integer increments from `0` per thunk evaluation.
11 xx # `L xx R` forms list from `L` thunk evaluated `R` times
12 .value + 1
13 }
It is unlikely that Raku is ever going to be faster than C++ for that kind of operation. It is still early in its life and there are lots of optimizations to be gained, but raw processing speed is not where it shines.
If you are trying to find the aliquot sum for all of the numbers in a continuous range then prime factorization is certainly less efficient than the method you arrived at. Sort of an inverse Sieve of Eratosthenes. There are a few things you could change to make it faster, though still probably much slower than C++
About twice as fast on my system:
constant $limit = 1_000_000;
my #s = 0,0;
#s.append: 1 xx $limit;
(2 .. $limit/2).race.map: -> $column {
loop ( my $row = (2 * $column); $row <= $limit; $row += $column ) {
#s[$row] += $column ;
}
}
say "s(", $limit, ") = ", #s[$limit] ; # s(1000000) = 1480437
Where the prime factorization method really shines is for finding arbitrary aliquot sums.
This produces an answer in fractions of a second when the inverse sieve would likely take hours. Using the Prime::Factor module: from the Raku ecosystem
use Prime::Factor;
say "s(", 2**97-1, ") = ", (2**97-1).&proper-divisors.sum;
# s(158456325028528675187087900671) = 13842607235828485645777841

Python: add zeroes in single digit numbers without using .zfill

Im currently using micropython and it does not have the .zfill method.
What Im trying to get is to get the YYMMDDhhmmss of the UTC.
The time that it gives me for example is
t = (2019, 10, 11, 3, 40, 8, 686538, None)
I'm able to access the ones that I need by using t[:6]. Now the problem is with the single digit numbers, the 3 and 8. I was able to get it to show 1910113408, but I need to get 19101034008 I would need to get the zeroes before those 2. I used
t = "".join(map(str,t))
t = t[2:]
So my idea was to iterate over t and then check if the number is less than 10. If it is. I will add zeroes in front of it, replacing the number . And this is what I came up with.
t = (2019, 1, 1, 2, 40, 0)
t = list(t)
for i in t:
if t[i] < 10:
t[i] = 0+t[i]
t[i] = t[i]
print(t)
However, this gives me IndexError: list index out of range
Please help, I'm pretty new to coding/python.
When you use
for i in t:
i is not index, each item.
>>> for i in t:
... print(i)
...
2019
10
11
3
40
8
686538
None
If you want to use index, do like following:
>>> for i, v in enumerate(t):
... print("{} is {}".format(i,v))
...
0 is 2019
1 is 10
2 is 11
3 is 3
4 is 40
5 is 8
6 is 686538
7 is None
another way to create '191011034008'
>>> t = (2019, 10, 11, 3, 40, 8, 686538, None)
>>> "".join(map(lambda x: "%02d" % x, t[:6]))
'20191011034008'
>>> "".join(map(lambda x: "%02d" % x, t[:6]))[2:]
'191011034008'
note that:
%02d add leading zero when argument is lower than 10 otherwise (greater or equal 10) use itself. So year is still 4digit string.
This lambda does not expect that argument is None.
I tested this code at https://micropython.org/unicorn/
edited :
str.format method version:
"".join(map(lambda x: "{:02d}".format(x), t[:6]))[2:]
or
"".join(map(lambda x: "{0:02d}".format(x), t[:6]))[2:]
second example's 0 is parameter index.
You can use parameter index if you want to specify it (ex: position mismatch between format-string and params, want to write same parameter multiple times...and so on) .
>>> print("arg 0: {0}, arg 2: {2}, arg 1: {1}, arg 0 again: {0}".format(1, 11, 111))
arg 0: 1, arg 2: 111, arg 1: 11, arg 0 again: 1
I'd recommend you to use Python's string formatting syntax.
>> t = (2019, 10, 11, 3, 40, 8, 686538, None)
>> r = ("%d%02d%02d%02d%02d%02d" % t[:-2])[2:]
>> print(r)
191011034008
Let's see what's going on here:
%d means "display a number"
%2d means "display a number, at least 2 digits"
%02d means "display a number, at least 2 digits, pad with zeroes"
so we're feeding all the relevant numbers, padding them as needed, and cut the "20" out of "2019".

How to calculate number of rows of a referenced array in Perl

A part of my code calculates inverse of a matrix (generated previously in the code) with dimensions more than 300 X 300. I want to use the elements of the inversed matrix further in the code. Have used the below code for this, trying with only 5X5 matrix for testing:
use strict;
use warnings;
use Math::MatrixReal;
my #a=(); #a is the matrix obtained
$a[0][0]=0.18761134;
$a[0][1]=0.010779401; #Have hard-coded the values here till $a[4][4]
my $ref_a = \#a;
my $b = Math::MatrixReal->new_from_rows($ref_a);
my $b_inv = $b->inverse();
print "\n Inverse is\n",$b_inv; #prints correct inverse
print "\n\nTest printing elements\n";
print $$b_inv[0][1][1],"\n"; #prints the correct element
my $row_b=scalar(#{$b});
print "Number of rows in b: ",$row_b,"\n"; #prints 6
my $col_b=#{$$b[0]};
print "Columns in b: ",$col_b,"\n"; #prints 5
my $row_binv=scalar(#$b_inv);
print "Number of rows in b_inv: ",$row_binv,"\n"; #prints 3
my $col_binv=#{$$b_inv[0]};
print "Number of columns in b_inv ",$col_binv,"\n"; #prints 5
I am not able to understand
why the output of number of rows for both b and b_inv is wrong? How to get the correct value of number of rows?
That although the syntax of printing elements of a referenced array is $$b_inv[1][1], I get the correct output when I use $$b_inv[0][1][1]
You are creating a
Math::MatrixReal
matrix object, and then accessing it as a simple Perl array. Poking around inside a Perl object indiscriminately is wrong, and you must use the methods defined in the documentation
In particular, your statement
print $$b_inv[0][1][1],"\n"; # prints the correct element
accesses a three-dimensional array, and there is no way of knowing what the "correct element" should be for this without reading the code of the module
This modification sets up a 5 x 5 identity matrix (in future, please provide data that we can use to reproduce your results) and takes its inverse. The values output are derived using the object's methods as I described and are all correct. Note that the rows and columns are indexed from one instead of from zero that you would expect for Perl arrays
use strict;
use warnings 'all';
use Math::MatrixReal;
my #arr = (
[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1],
);
my $ref_a = \#arr;
my $b = Math::MatrixReal->new_from_rows(\#arr);
my $b_inv = $b->inverse;
print "\nInverse is\n", $b_inv;
print "\n\nTest printing elements\n";
print $b_inv->element($_, $_), "\n" for 1 .. 5;
my ($row_b, $col_b) = $b->dim;;
print "Number of rows in b: $row_b\n"; # prints 5
print "Columns in b: $col_b\n"; # prints 5
my ($row_binv, $col_binv) = $b_inv->dim;;
print "Number of rows in b_inv: $row_binv\n"; # prints 5
print "Number of columns in b_inv $col_binv\n"; # prints 5
output
Inverse is
[ 1.000000000000E+000 0.000000000000E+000 0.000000000000E+000 0.000000000000E+000 0.000000000000E+000 ]
[ 0.000000000000E+000 1.000000000000E+000 0.000000000000E+000 0.000000000000E+000 0.000000000000E+000 ]
[ 0.000000000000E+000 0.000000000000E+000 1.000000000000E+000 0.000000000000E+000 0.000000000000E+000 ]
[ 0.000000000000E+000 0.000000000000E+000 0.000000000000E+000 1.000000000000E+000 0.000000000000E+000 ]
[ 0.000000000000E+000 0.000000000000E+000 0.000000000000E+000 0.000000000000E+000 1.000000000000E+000 ]
Test printing elements
1
1
1
1
1
Number of rows in b: 5
Columns in b: 5
Number of rows in b_inv: 5
Number of columns in b_inv 5

Eulerian path, directed graph

Good evening. I'm really a beginner, and I am working in an implementation of an Eulerian path. That means that every edge (not a vertex) of a directed graph has to be used only once.
For some reason it doesn't manage to go over all vertices even if in paper it should. It seems to ignore half of the vertices or simply not adding them to the circuit.
The expected outcome is:
6->7->8->9->6->3->0->2->1->3->4
Nevertheless, the outcome I get is:
6 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 9 9 9 9 9 9 9 3 3 3 3 3 3
What i have as code is the following:
my %edges={'6'=>['3','7'],'8'=>['9'],'1'=>['3'],'0'=>['2'],'3'=>['0','4'], '7' =>['8'],'9'=>['6'],'2'=>['1']};
my $startvertex=6; #this i got from additional code
my $location=$startvertex;
my #stack = ($startvertex);
my #circuit = ();
while (#stack)
{
if (#{$edges{$location}}[0])
{
push #stack, $location;
my $newlocation=#{$edges{$location}}[0];
splice #{$edges{$location}},0,1;
$location=$newlocation;
}
else
{
push #circuit, $location;
$location=pop #stack;
}
}
my #revcircuit= reverse #circuit;
print #revcircuit;
Thank you very much in advance for your insight.
The problem is here:
if (#{$edges{$location}}[0])
One of your nodes is called 0 which is false in Perl. Therefore, once the zero node is reached, the program continues as if there were no more nodes.
Use defined instead.
Here's a working version, slightly edited (e.g. removed unnecessary array dereference):
#!/usr/bin/perl
use warnings;
use strict;
my %edges = ( 6 => [3, 7],
8 => [9],
1 => [3],
0 => [2],
3 => [0, 4],
7 => [8],
9 => [6],
2 => [1]);
my $startvertex = 6;
my $location = $startvertex;
my #stack = ($startvertex);
my #circuit;
while (#stack) {
if (defined $edges{$location}[0]) {
push #stack, $location;
$location = shift #{ $edges{$location} };
} else {
push #circuit, $location;
$location = pop #stack;
}
}
my #revcircuit = reverse #circuit;
print "#revcircuit\n";
Also note that it uses round parentheses to define the %edges hash. Curly braces introduce a hash reference, with them I'm getting
Reference found where even-sized list expected at ./1.pl line 5.

How to change separator in CoffeeScript generator?

Following code:
yearsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld
"#{child} is #{age}"
will return:
max is 10, ida is 9, tim is 11
How to make it return a value without commas? Like that:
max is 10 ida is 9 tim is 11
There are no "commas" in ages.
ages is an array, and if you write an array to the console, the default behavior is to display it with commas. If you don't want commas, you can use Array#join produce a string separating the values with whatever separator you want:
yearsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld
"#{child} is #{age}"
agesString = ages.join ' '
console.log agesString
c.coffee
yearsOld = max: 10, ida: 9, tim: 11
ages = (input)->
output=""
for k,v of input
output += k + " is " + v + " "
return output
console.log ages yearsOld
run
coffee c.coffee
max is 10 ida is 9 tim is 11
If you want a "one-liner":
c.coffee
yearsOld = max: 10, ida: 9, tim: 11
console.log (("#{k} is #{v}") for k, v of yearsOld).join(' ')
run
coffee c.coffee
max is 10 ida is 9 tim is 11
If you want to filter, for example not including ida:
console.log ((("#{k} is #{v}") if k != "ida") for k, v of yearsOld ).join(' ').replace(' ', ' ').replace(/^\s/, '')
or only above 10 years old:
console.log ((("#{k} is #{v}") if v > 10) for k, v of yearsOld ).join(' ').replace(' ', ' ').replace(/^\s/, '')