perl - algorithm to replace multiple if statements using mod - perl

I have a perl script that parses a large files. It works but it is slow and I see a pattern that i'd like to take advantage of, but i don't know how to write it.
there is a section where i count a number of objectIds, and have to return a value Spaces. The mininum number of *objectIds is 3 and increases in odd increments, my output starts at 3 and increases in multiples of three.
So i have a chain of 30 statements like this
if($objectIds == 3)
{
$spaces = 3;
}
if($objectIds == 5)
{
$spaces = 6;
}
if($objectIds == 7)
{
$spaces = 9;
}
I see that the difference is incrementing by a modulo of 1, i.e. (3 % 3 = 0), (6 % 5 = 1), (9 % 7 = 2), but i can't for the life of me figure out how to optimize this.

This formula should calculate and replace your ifs,
# $spaces = $objectIds + ($objectIds-3)/2;
# $spaces = (2*$objectIds + $objectIds-3)/2;
# $spaces = 3*($objectIds -1)/2;
$spaces = ($objectIds -1) * 3/2;

The first optimisation I see is to use elsif :
if($objectIds == 3)
{
$spaces = 3;
}
elsif($objectIds == 5)
{
$spaces = 6;
}
elsif($objectIds == 7)
{
$spaces = 9;
}

Related

How to break/escape from inner loop in Swift

I'm looking for a way to stop iterating the just inner loop once a condition is met. I thought of using "continue" but it's not doing what i wanted.
"break" seems to break the entire loop including the outer loop.
So in my code, once the condition is met. I want to stop iterating j but i want to start iterating i again. Thank you,
for i in 0..<sortedArray.count{
for j in 1..<sortedArray.count{
if sortedArray[j] == sortedArray[i]{
//I want to skip iterating inner loop j from now. and back to iterating i
}
}
}
Break just breaks the inner loop.
e.g.
for var i in 0...2
{
for var j in 10...15
{
print("i = \(i) & j = \(j)")
if j == 12
{
break;
}
}
}
Output -->
i = 0 & j = 10
i = 0 & j = 11
i = 0 & j = 12
i = 1 & j = 10
i = 1 & j = 11
i = 1 & j = 12
i = 2 & j = 10
i = 2 & j = 11
i = 2 & j = 12
The nested index loop and subsequent element access by index approach (anArray[accessByThisIndex]) is kind of C++'ish rather than Swifty. Depending on you application, you can make good use of neat features in Swift to achieve your goal.
E.g., assuming (based on the anme of your array) your array is sorted,
let sortedArray = [2, 5, 7, 9]
for i in sortedArray {
for j in sortedArray where j <= i {
print(j)
}
print("-----")
}
/* 2
-----
2
5
-----
2
5
7
-----
2
5
7
9
----- */
Note that we don't care about the indices of the array above, instead accessing the elements of the array directly in the for ... in (... where ...) loop(s).
Another alternatively coulkd make use of prefixUpTo(:_)
for i in 1...sortedArray.count {
sortedArray.prefixUpTo(i).forEach {
print($0)
}
print("-----")
}
/* same printout as above */

speed up prime number generating

I have written a program that generates prime numbers . It works well but I want to speed it up as it takes quite a while for generating the all the prime numbers till 10000
var list = [2,3]
var limitation = 10000
var flag = true
var tmp = 0
for (var count = 4 ; count <= limitation ; count += 1 ){
while(flag && tmp <= list.count - 1){
if (count % list[tmp] == 0){
flag = false
}else if ( count % list[tmp] != 0 && tmp != list.count - 1 ){
tmp += 1
}else if ( count % list[tmp] != 0 && tmp == list.count - 1 ){
list.append(count)
}
}
flag = true
tmp = 0
}
print(list)
Two simple improvements that will make it fast up through 100,000 and maybe 1,000,000.
All primes except 2 are odd
Start the loop at 5 and increment by 2 each time. This isn't going to speed it up a lot because you are finding the counter example on the first try, but it's still a very typical improvement.
Only search through the square root of the value you are testing
The square root is the point at which a you half the factor space, i.e. any factor less than the square root is paired with a factor above the square root, so you only have to check above or below it. There are far fewer numbers below the square root, so you should check the only the values less than or equal to the square root.
Take 10,000 for example. The square root is 100. For this you only have to look at values less than the square root, which in terms of primes is roughly 25 values instead of over 1000 checks for all primes less than 10,000.
Doing it even faster
Try another method altogether, like a sieve. These methods are much faster but have a higher memory overhead.
In addition to what Nick already explained, you can also easily take advantage of the following property: all primes greater than 3 are congruent to 1 or -1 mod 6.
Because you've already included 2 and 3 in your initial list, you can therefore start with count = 6, test count - 1 and count + 1 and increment by 6 each time.
Below is my first attempt ever at Swift, so pardon the syntax which is probably far from optimal.
var list = [2,3]
var limitation = 10000
var flag = true
var tmp = 0
var max = 0
for(var count = 6 ; count <= limitation ; count += 6) {
for(var d = -1; d <= 1; d += 2) {
max = Int(floor(sqrt(Double(count + d))))
for(flag = true, tmp = 0; flag && list[tmp] <= max; tmp++) {
if((count + d) % list[tmp] == 0) {
flag = false
}
}
if(flag) {
list.append(count + d)
}
}
}
print(list)
I've tested the above code on iswift.org/playground with limitation = 10,000, 100,000 and 1,000,000.

Basic round robin in Perl difference between (++ / +1)

Recently I was trying to make a simple round robin with Perl , and I found a behaviour that I don't understand clearly.
Here the behaviour:
my $a = {index => 0};
for (0 .. 10) {
$a->{index} = ($a->{index}++) % 2;
warn $a->{index};
}
The output of this code will be:
0,0,0,..,0
But if I do the "same" code replacing $a->{index}++ by $a->{index}+1 , the round robin will be fine, example
my $a = {index => 0};
for (0 .. 10) {
$a->{index} = ($a->{index}+1) % 2;
warn $a->{index};
}
The output will be:
1,0,1,0,1,0,1,0...
Someone can explain me the difference between ++ / +1 in this case? I find this really "ugly", because if I don't assign the result to any variable in the case "++" the code will work as expected unless I put the sum inside ().
This code will do a round robin correctly:
my $a = {index => 0};
for (0 .. 10) {
warn $a->{index}++ % 2;
}
With () in the sum, the code will output: 1,2,3,4,5,6,7,8,9
my $a = {index => 0};
for (0 .. 10) {
warn ($a->{index}++) % 2;
}
$a->{index}+1 returns $a->{index}+1, while
$a->{index}++ returns $a->{index} before it was changed.
++$a->{index} returns $a->{index}+1, but it makes no sense to use it in that expression since it needlessly changes $a->{index}.
$a->{index} = ($a->{index}+1) % 2;
Say $a->{index} is initially 0.
$a->{index}+1 returns 1.
Then you assign 1 % 2, which is 1 to $a->{index}.
$a->{index} = $a->{index}++ % 2;
Say $a->{index} is initially 0.
$a->{index}++ sets $a->{index} to 1 and returns 0 (the old value).
Then you assign 0 % 2, which is 0 to $a->{index}.
Options:
$a->{index} = ( $a->{index} + 1 ) % 2;
if ($a->{index}) {
...
}
or
$a->{index} = $a->{index} ? 0 : 1;
if ($a->{index}) {
...
}
or
$a->{index} = !$a->{index};
if ($a->{index}) {
...
}
or
if (++$a->{index} % 2) {
...
}
or
if ($a->{index}++ % 2) {
...
}
Note that the last two options leaves an ever-increasing value in $a->{index} rather than 0 or 1.
Note that the last two options differ in whether the condition will be true or false on the first pass.

Consistent random colour highlights

In a table I have columns with to and from dates, I highlight overlaps between rows taking into account the periods, this is done exhaustively in nested loops. This is not the issue.
I need the same colour for the rows that overlap.
sub highlight_overlaps {
my $date_from1;
my $date_to1;
my $date_from2;
my $date_to2;
my $i = 0;
my $j = 0;
for ($i; $i < $#DATE_HOLDER; $i++) {
$date_from1 = $DATE_HOLDER[$i][0];
$date_to1 = $DATE_HOLDER[$i][1];
my $red = int(rand(65)) + 190;
my $green = int(rand(290)) - 55;
my $blue = int(rand(290)) - 55;
for ($j=$i+1; $j<=$#DATE_HOLDER; $j++) {
$date_from2 = $DATE_HOLDER[$j][0];
$date_to2 = $DATE_HOLDER[$j][1];
if (($date_from1 le $date_to2 && $date_to1 ge $date_to2) ||
($date_from1 le $date_from2 && $date_to1 le $date_to2) ||
($date_from1 gt $date_from2 && $date_from1 lt $date_to2)) {
$tb->setCellStyle($i+2, 6, "background-color:rgb($red,$green,$blue);font-size:9pt");
$tb->setCellStyle($i+2, 7, "background-color:rgb($red,$green,$blue);font-size:9pt");
$tb->setCellStyle($j+2, 6, "background-color:rgb($red,$green,$blue);font-size:9pt");
$tb->setCellStyle($j+2, 7, "background-color:rgb($red,$green,$blue);font-size:9pt");
}
}
}
}
This works fine if it's just a pair of dates; say:
1) 25-06-2012 27-06-2012
2) 18-06-2012 29-06-2012
Will get the same colour
If though I have
0) 26-06-2012 28-06-2012
1) 25-06-2012 27-06-2012
2) 18-06-2012 29-06-2012
0 will get a different colour while 1 & 2 are paired as intended.
When and how to pick colours so that different colours are only applied to different overlaps?
Following up on the first answer; how may I represent overlaps in order to store them in a data structure, so that I can colour them after their detection?
You'll have to compare each interval against each other interval, and put them in 'buckets' when they are equal. Now when you compare an interval to a third interval, you put the third in the same bucket as the interval.
Then you print the buckets.
Perl's hash would make for fine buckets.
About your overlap detection
There is no overlap if
date1_to < date2_from OR
date2_to < date1_from
Or, in Perl:
if ($date_to1 lt $date_from2 || $date_to2 lt $date_from1) {
#overlap
}
Invert that either using Perl's unless, or using de Morgan:
if ($date_to1 ge $date_from2 && $date_to2 ge $date_from1) {
#overlap
}

simple number series

This is a simple number series question, I have numbers in series like
2,4,8,16,32,64,128,256 these numbers are formed by 2,2(square),2(cube) and so on.
Now if I add 2+4+8 = 14. 14 will get only by the addition 2,4 and 8.
so i have 14in my hand now, By some logic i need to get the values which are helped to get 14
Example:
2+4+8 = 14
14(some logic) = 2,4,8.
This is an easy one:
2+4+8=14 ... 14+2=16
2+4+8+16=30 ... 30+2=32
2+4+8+16+32=62 ... 62+2=64
So you just need to add 2 to your sum, then calculate ld (binary logarithm), and then subtract 1. This gives you the number of elements of your sequence you need to add up.
e.g. in PHP:
$target=14;
$count=log($target+2)/log(2)-1;
echo $count;
gives 3, so you have to add the first 3 elements of your sequence to get 14.
Check the following C# code:
x = 14; // In your case
indices = new List<int>();
for (var i = 31; i >= i; i--)
{
var pow = Math.Pow(2, i);
if x - pow >= 0)
{
indices.Add(pow);
x -= pow;
}
}
indices.Reverse();
assuming C:
unsigned int a = 14;
while( a>>=1)
{
printf("%d ", a+1);
}
if this is programming, something like this would suffice:
int myval = 14;
int maxval = 256;
string elements = "";
for (int i = 1; i <= maxval; i*=2)
{
if ((myval & i) != 0)
elements += "," + i.ToString();
}
Use congruency module 2-powers: 14 mod 2 = 0, 14 mod 4 = 2, 14 mod 8 = 6, 14 mod 16 = 14, 14 mod 32 = 14...
The differences of this sequence are the numbers you look for 2 - 0 = 2, 6 - 2 = 4, 14 - 6 = 8, 14 - 14 = 0, ...
It's called the p-adic representation and is formally a bit more difficult to explain, but I hope this gives you an idea for an algorithm.