Build Lowest Number by Removing n digits from a given number - arrays

It takes as input a number(string) and then the task is to remove n numbers to give a resulting lowest possible number, but you have to take care of the order, that is the constraint. You cannot change the order of the original numbers.
I wanted it to work in O(n), so I did this:
#!/usr/bin/perl
#lowestNS.pl
#Date: 2016-06-28
use warnings;
use strict;
use utf8;
(#ARGV == 2) or die "2 args needed";
my $num = $ARGV[0];
my $d = $ARGV[1];
my #arr;
int($num) > 0 or die "Enter positive number";
print "Number in: $num\nDel: $d\n";
if(int($num) == 0) {
print "Result: 0\n";
exit;
}
else {
my $str = $num;
#arr = split(//, $str); #/Split on each number
#Split and multiply by reverse index, to give precedence to the order of numbers
for(my $i = 0; $i < #arr; $i++) {
$arr[$i] *= (#arr - $i);
}
}
print "arr: " . join(',' , #arr) . "\n";
for (my $j = 0; $j < $d; $j++) {
my $max = $arr[0];
my $m_index = -1;
#replace nth maximum with -1
for (my $i = 0; $i < #arr; $i++) {
if($max <= $arr[$i]) {
$max = $arr[$i];
$m_index = $i;
}
}
$arr[$m_index] = -1;
}
#return all numbers with value other than -1
my $result = "";
for (my $i = 0; $i < #arr; $i++) {
if($arr[$i] != -1){
$result = $result . "" . $arr[$i]/(#arr - $i);
}
}
print "Result: $result\n";
It works in all cases, except, cases like:
Number = 765028321
Delete = 5
The problem is the removal of 765028321 when it should have removed the 765028321.
because 2*5 > 3*3.

I think, the algorithm is straightforward:
Suppose, N is the number of digits to delete;
1. Find the first smallest digit in the first N digits, delete digits to the left of it, decrease N by the number of digits deleted.
2. if N>0 take the digits to the right and repeat the steps above.
Of course, we need to check for marginal cases, and to ensure the final number does not start with 0.
Here is the code draft:
#!/usr/bin/perl
use strict;
use warnings;
my ($number, $del)=#ARGV;
my #num = $number=~m{.}g;
my $curpos=0;
my #finalnum;
for(;;) {
last if $del <=0 || $curpos+$del>=#num;
my $minpos=$curpos;
for (my $i=$curpos;$i<$curpos+$del+1 && $i < #num;$i++) {
if ($num[$i]<$num[$minpos] && !($curpos==0 && $num[$i]==0)) {
$minpos=$i;
}
}
push #finalnum, $num[$minpos];
$del-=($minpos-$curpos);
$curpos=$minpos+1;
}
push #finalnum, #num[$curpos+$del..$#num] if $curpos+$del < #num;
print join '', #finalnum;

A possible solution can be:
Create an array having count of all digits, eg. in your case, this array will look like: 1,1,2,1,0,1,1,1,1,0
Then, in a greedy manner, remove all big digits, as many as can be done. So, you remove 9s, but since its count is 0, you still need to remove 5 digits. Remove 8s, so you still need to remove 4 digits and so on.
This case is kind of trivial, so you will be left with 2 2's, 1 1's and 1 0's. In there were, say 3 4's also in the number, you will then need to remove 1 4. So, you will remove the leftmost of partial removals required.
Its a greedy approach, and works in O(n).

Related

how to build a loop that write previous number's 1.8 times

How to write a for loop which has the step of 1.8
for example
for ($i = 1; $i <= 11; $i++) {echo $i;}
$i should give me an output like : 1, 1.8, 3.24, 5.8 , 10.5
$i++ means that you are incrementing the value of $i variable by 1 unit (the initial value you assigned to 1. Each following iteration it will be increased by 1 until it reaches 11 (including 11)). If I understood correctly the question, you want to have a step of 1.8
for ($i = 1; $i <= 11; $i= $i * 1.8) {
echo $i;
}
Or another alternative would be:
$result = 1;
for ($i = 1; $i <= 11; $i++) {
$result *= 1.8;
echo $result;
}
In Java it's simple as:
for (double i = 1; i <= 11; i *= 1.8) {
System.out.println(i);
}
I guess you can convert it to the language you use.

Fixing MEL FOR loop

I have been stuck doing this for if loop for the last few days.
Basically, it's a loop that starts at the height of 6 and decreases within the FOR loop, when the height (y) hits 1 I want it to start increasing by 1.
At the moment the loop hits 1, the height increases by 1 but then because it loops back around it does not increase the next shape by another 1.
I know its a bit confusing but I hope I can grab some help!
Thanks!
int $f = 6;
for ($e = 24 ; $e <= 31; $e++)
{
if ($f <= 1) {
string $currentObject = $objects[$e];
select -r $currentObject ;
setAttr ($currentObject+".sy") ($f++);
}
else {
string $currentObject = $objects[$e];
select -r $currentObject ;
setAttr ($currentObject+".sy") ($f--);
}
}
If I understand your problem correctly, you only want to switch the direction of increment or decrement at the extremes, not between, i. e. you need another variable that codes the direction (incrementing or decrementing). I called that $diff in this code:
int $f = 6;
int $diff = -1;
for ($e = 24 ; $e <= 31; $e++)
{
if ($f <= 1) {
$diff = 1;
} else if ($f >= 6) {
$diff = -1;
}
string $currentObject = $objects[$e];
select -r $currentObject ;
setAttr ($currentObject+".sy") ($f);
$f = $f + $diff;
}

Longest pair of string elements or longest side of the pairs in Bash array

Given an array in Bash:
my_array=("a" "b" "a bc" "d" "ab" "cd")
Considering each two consecutive items in the array as a pair, how can I:
Get the length of the longest pair. In the example above it would be "a bc" "d", so length 5.
Get the length of the longest of the fist items in the pairs. Above would be "a bc", so length 4.
Get the length of the longest of the second items in the pairs. Above would be "cd", so length 2.
The trick is to use a for loop that increments an index variable by 2 every time. Everything else is basic parameter expansion to get the lengths of elements and tests against the current maximums.
#!/usr/bin/env bash
declare -a my_array=("a" "b" "a bc" "d" "ab" "cd")
declare -i maxlen_combo=0 maxlen_first=0 maxlen_second=0
IFS=""
for (( i = 0; i < ${#my_array[#]}; i += 2 )); do
if [[ $maxlen_first -lt ${#my_array[i]} ]]; then
maxlen_first=${#my_array[i]}
fi
if [[ $maxlen_second -lt ${#my_array[i+1]} ]]; then
maxlen_second=${#my_array[i+1]}
fi
combo="${my_array[*]:i:2}"
if [[ $maxlen_combo -lt ${#combo} ]]; then
maxlen_combo=${#combo}
fi
done
echo "Maximum pair length: $maxlen_combo"
echo "Maximum first length: $maxlen_first"
echo "Maximum second length: $maxlen_second"
You can use
printf "%s\n" ${my_array[#]} |
awk '{itemlen=length($0)}
NR%2==1 {
firstmaxlen = (itemlen > firstmaxlen) ? itemlen : firstmaxlen;
firsthalf=itemlen
}
NR%2==0 {
secondmaxlen = (itemlen > secondmaxlen) ? itemlen : secondmaxlen;
pairlen = (firsthalf + itemlen > pairlen ) ? firsthalf + itemlen : pairlen ;
}
END { printf ("maxcombo, maxfirst, maxsecond) = (%d, %d, %d)\n",
pairlen, firstmaxlen, secondmaxlen) }
'
In this case a small function can help:
printf "%s\n" ${my_array[#]} |
awk 'function max(a,b) { return (a>b ? a : b) }
{itemlen=length($0)}
NR%2==1 {
firstmaxlen = max(itemlen, firstmaxlen);
firsthalf=itemlen
}
NR%2==0 {
secondmaxlen = max(itemlen, secondmaxlen);
pairlen = max(firsthalf + itemlen, pairlen);
}
END { printf ("maxcombo, maxfirst, maxsecond) = (%d, %d, %d)\n",
pairlen, firstmaxlen, secondmaxlen) }
'

Perl: Using hash instead of array

while ($word = <STDIN>) {
$length = length($word) -1; # Subtract 1 for included newline char
$wordLength[$length]++;
}
print "Word length \t\t Occurrences \n\n";
for ( my $i =1; $i <= $#wordLength; $i++ ) {
if (not exists $wordLength[$i]) {
print "$i \t\t\t 0 \n";
}
else {
print "$i \t\t\t $wordLength[$i] \n";
}
}
This works great reading in a txt file and outputting as such:
Word Length Occurrence
1 27
2 104
3 1039
4 3505
5 7181
6 11765
7 15898
I am trying to get this to work using hash instead of an array but it doesn't seem to work. This is my attempt:
while ($word = <STDIN>) {
chomp($word);
$length = length($word);
$wordLength{$word} = "$length";
}
foreach $word (sort keys %wordLength) {
print "$word, $wordLength{$word}\n"; # print key and value
}
Why? Any array works great here.
my #occurrences_by_length;
while (my $word = <>) {
chomp($word);
my $length = length($word);
++$occurrences_by_length[$length];
}
print "Length Occurrences\n";
for my $length (1..$#occurrences_by_length) {
my $occurrences = $occurrences_by_length[$length]
or next;
printf "%6d %11d\n", $length, $occurrences;
}
A hash, while less efficient, could easily be used with next to no changes.
my %occurrences_by_length;
while (my $word = <>) {
chomp($word);
my $length = length($word);
++$occurrences_by_length{$length};
}
print "Length Occurrences\n";
for my $length (sort { $a <=> $b } keys(%occurrences_by_length)) {
my $occurrences = $occurrences_by_length{$length};
printf "%6d %11d\n", $length, $occurrences;
}

display specific values form sorted array

this is my sorted array
$sortedArray = array($value_one_fin,$value_two_fin,$value_three_fin,$value_four_fin,$value_five_fin,$value_six_fin,$value_seven_fin,$value_eight_fin,$value_nine_fin,$value_ten_fin,$value_eleven_fin,$value_twelve_fin,$value_thirteen_fin,$value_fourteen_fin,$value_fifteen_fin,$value_sixteen_fin,$value_seventeen_fin,$value_eighteen_fin,$value_nineteen_fin,$value_twenty_fin,$value_twentyone_fin,$value_twentytwo_fin,$value_twentythree_fin,$value_twentyfour_fin);
$result=findClosest($sortedArray,$num1);
this is the variable where i store my entered number
$num1
what i want to do is to display values from sorted array in the manner Buy at / above: 102.51 Targets: 105,107.58,110.19,112.83
Stoploss : 100
in the above example i have entered 100 as $num1. as per calc the $result is also 100 so it has to display next greater closest number which is 102.51 and in stop loss it shuld display next smaller closest number
CONDITION
1>if entered number $num1 is greater than $result then display the next greater closest number from sorted array
2>if entered number $num1 is equal to $result then display the next greater closest number from sorted array
pls help me with this how can i do this im doing this
Buy At/Above ";if ($num1>$result) {echo "$result ";}if ($num1=$result) {echo "$result ";}if ($num1<$result) {echo "$result ";}echo "for Targets"; if ($result<$value_one_fin){echo " $value_one_fin,";}
if ($result<$value_two_fin){echo " $value_two_fin,";}
if ($result<$value_three_fin){echo " $value_three_fin,";}
if ($result<$value_four_fin){echo " $value_four_fin,";}
if ($result<$value_five_fin){echo " $value_five_fin,";}
if ($result<$value_six_fin){echo " $value_six_fin,";}
if ($result<$value_seven_fin){echo " $value_seven_fin,";}
if ($result<$value_eight_fin){echo " $value_eight_fin,";}
if ($result<$value_nine_fin){echo " $value_nine_fin,";}
if ($result<$value_ten_fin){echo " $value_ten_fin,";}
if ($result<$value_eleven_fin){echo " $value_eleven_fin,";}
if ($result<$value_twelve_fin){echo " $value_twelve_fin,";}
if ($result<$value_thirteen_fin){echo " $value_thirteen_fin,";}
if ($result<$value_fourteen_fin){echo " $value_fourteen_fin,";}
if ($result<$value_fifteen_fin){echo " $value_fifteen_fin,";}
if ($result<$value_sixteen_fin){echo " $value_sixteen_fin,";}
if ($result<$value_seventeen_fin){echo " $value_seventeen_fin,";}
if ($result<$value_eighteen_fin){echo " $value_eighteen_fin,";}
if ($result<$value_nineteen_fin){echo " $value_nineteen_fin,";}
if ($result<$value_twenty_fin){echo " $value_twenty_fin,";}
if ($result<$value_twentyone_fin){echo " $value_twentyone_fin,";}
if ($result<$value_twentytwo_fin){echo " $value_twentytwo_fin,";}
if ($result<$value_twentythree_fin){echo " $value_twentythree_fin,";}
if ($result<$value_twentyfour_fin){echo " $value_twentyfour_fin";}
but its not working and also how to display STOP LOSS
Apologies if I've misunderstood the question - I'm not 100% clear what you're trying to achieve, but I'll try to be helpful.
It looks as though your array is sorted smallest to largest. So to find the first value greater than or equal to $num1, simply loop through your array from beginning to end, and stop when you find a value >= $num1 e.g.
function findClosest($sortedArray, $num1) {
foreach ($sortedArray as $value) {
if ($value >= $num1) {
return $value;
}
}
}
To find the closest number less than or equal to $num1, loop through the array from the end to the beginning e.g.
function findClosestLessThanOrEqual($sortedArray, $num1) {
for ($i = sizeOf($sortedArray) - 1; $i >= 0; $i--) {
if ($sortedArray[$i] <= $num1) {
return $sortedArray[$i];
}
}
}
There are faster ways to search a large array, but if your array only contains 24 values I think that's the simplest way.
Just needs a slight tweak to get the first 5 items >= $num1:
function find5Closest($sortedArray, $num1) {
for ($i = 0; $i < sizeOf($sortedArray); $i++) {
if ($sortedArray[$i] >= $num1) {
return array_slice($sortedArray, $i, 5);
}
}
}
This should return an array up to 5 values. If there are only 3 values >= $num1, then it will only return 3 values; if no values are >= $num1 it will return an empty array.
Similarly:
function find5ClosestLessThanOrEqual($sortedArray, $num1) {
for ($i = sizeOf($sortedArray) - 1; $i >= 0; $i--) {
if ($sortedArray[$i] <= $num1) {
return array_slice($sortedArray, min(0, $i-4), 5);
}
}
}

Resources