How to write a logic using for loop or while loop for printing Armstrong numbers?
Someone kindly explain how to print Armstrong numbers between 1 to 1,00,00,000.
This the algorithm that I followed
step 1 : initializing variable min,max,n,sum,r,t
step 2 : my $n = <>;
step 3 : to find base of $n
step 4 : using for loop
( for (n = min; n < max ; n++ )
step 5 : some logic like
n=t,sum =0,r=t%10,t=n/10,
step 6 :
sum = sum + (n ^ base );
step 6 :
if ( sum == num ) print Armstrong numbers else not.
I tried to code this my code look like this
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
my $n;
chomp($n);
my $min = 1;
my $max = 10000000
my $r;
my $sum;
my $t;
my $base = length($n);
print "base is $base\n";
for ($n = $min; $n <= $max; $n++) {
$t = $n;
$sum = 0;
while ($t != 0) {
$r = $t % 10;
$t = $t / 10;
{
$sum = $sum + ($base * $r);
}
if ($sum == $n) {
print "$n\n";
}
}
}
Several things:
It's bad practice to declare something with my until you need it.
You must remember that numbers are also strings, and can be manipulated by string functions like split.
C-like loops are discouraged in Perl because they're hard to read.
Constants should be ...well... constant.
Here's my attempt. I use split to split up my digits into an array of digits. This is a lot easier than dividing constantly by ten. I can get the number of digits by simply taking the scalar value of my #digits array.
I can then loop through #digits, taking each one to the power of $power and adding it to sum. I use the map command for this loop, but I could have used another for loop too.
#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);
use constant {
MIN => 1,
MAX => 1_000_000,
};
for my $number ( (+MIN..+MAX) ) {
my #digits = split //, $number;
my $power = #digits;
my $sum = 0;
map { $sum += $_**$power } #digits;
if ( $sum == $number ) {
say "$number is an Armstrong number";
}
}
And my output:
1 is an Armstrong number
2 is an Armstrong number
3 is an Armstrong number
4 is an Armstrong number
5 is an Armstrong number
6 is an Armstrong number
7 is an Armstrong number
8 is an Armstrong number
9 is an Armstrong number
153 is an Armstrong number
370 is an Armstrong number
371 is an Armstrong number
407 is an Armstrong number
1634 is an Armstrong number
8208 is an Armstrong number
9474 is an Armstrong number
54748 is an Armstrong number
92727 is an Armstrong number
93084 is an Armstrong number
548834 is an Armstrong number
Took a bit over five seconds to run.
Instead of map, I could have done this loop:
for my $digit ( #digits ) {
$sum = $sum + ( $digit ** $power);
}
Did this one at university...
I dug out the one I made in C and converted it to perl for you (it may not be the best way to do this, but it is the way I did it):
#!/usr/bin/env perl
use strict;
use warnings;
my $min = 1;
my $max = 10000000;
for (my $number = $min; $number <= $max; $number++) {
my #digits = split('', $number);
my $sum = 0;
foreach my $digit (#digits) {
$sum += $digit**length($number);
}
if ($sum == $number) {
print "$number\n";
}
}
(Demo - 1 to 9999 due to execution time limit)
Your code seems to be right, but you have some kind of problems with your start. For example you dont read from STDIN or from #ARGV. Would you do that, you just have a small problem with your calculating of the exponential calculation. In most Programming Languages, the syntax for a exponential calculation is ** or a pow() function.
I really dont understand, for what this part is:
while ($t != 0) {
$r = $t % 10;
$t = $t / 10;
{
$sum = $sum + ($base * $r);
}
if ($sum == $n) {
print "$n\n";
}
}
For what is the naked block? Why do you use the modulus? .. Well i give you a small code for calculating the armstrong numbers with bases of 1..100, between 0 and 10million:
#!/usr/bin/perl
use strict;
use warnings;
foreach my $base (0..100) { # use the foreach loop as base
for my $num (0..10_000_000) { # and use numbers between this range
my $ce=0; # ce = calculated exp.
foreach my $num2 (split //,$num ) { # split each number for calc each
$ce += $num2 ** $base; # exp. and adding it (see algorithm)
}
if ($num == $ce) { # when the exp. num and the number
print "$base => $num\n"; # itself equals, its a armstrong number
} # print that
}
}
Related
I have an array of 11 elements. Where I want to sum the odd elements including the first and last elements as one scalar and the evens as another.
This is my code I am trying to use map adding 2 to each index to achieve the result but I think I have got it wrong.
use strict;
use warnings;
use Data::Dumper;
print 'Enter the 11 digiet serial number: ';
chomp( my #barcode = //, <STDIN> );
my #sum1 = map { 2 + $_ } $barcode[1] .. $barcode[11];
my $sum1 = sum Dumper( \#sum1 );
# sum2 = l2 + l4 + l6 + r8 + r10;
printf "{$sum1}";
What is a good way to achieve this?
Sum of even/odd indicies (what you asked for, but not what you want[1]):
use List::Util qw( sum ); # Or: sub sum { my $acc; $acc += $_ for #_; $acc }
my $sum_of_even_idxs = sum grep { $_ % 2 == 0 } 0..$#nums;
my $sum_of_odd_idxs = sum grep { $_ % 2 == 1 } 0..$#nums;
Sum of even/odd values (what you also asked for, but not what you want[1]):
use List::Util qw( sum ); # Or: sub sum { my $acc; $acc += $_ for #_; $acc }
my $sum_of_even_vals = sum grep { $_ % 2 == 0 } #nums;
my $sum_of_odd_vals = sum grep { $_ % 2 == 1 } #nums;
Sum of values at even/odd indexes (what you appear to want):
use List::Util qw( sum ); # Or: sub sum { my $acc; $acc += $_ for #_; $acc }
my $sum_of_vals_at_even_idxs = sum #nums[ grep { $_ % 2 == 0 } 0..$#nums ];
my $sum_of_vals_at_odd_idxs = sum #nums[ grep { $_ % 2 == 1 } 0..$#nums ];
Given that you know how many elements you have, you could use the following:
use List::Util qw( sum ); # Or: sub sum { my $acc; $acc += $_ for #_; $acc }
my $sum_of_vals_at_even_idxs = sum #nums[0,2,4,6,8,10];
my $sum_of_vals_at_odd_idxs = sum #nums[1,3,5,7,9];
I included these in case someone needing these lands on this Q&A.
Add up values at odd and at even indices
perl -wE'#ary = 1..6;
for (0..$#ary) { $_ & 1 ? $odds += $ary[$_] : $evens += $ary[$_] };
say "odds: $odds, evens: $evens"
'
Note for tests: with even indices (0,2,4) we have (odd!) values (1,3,5), in this (1..6) example
You can use the fact that the ?: operator is assignable
print 'Enter the 11 digiet serial number: ';
chomp( my #barcode = //, <STDIN> );
my $odd = 0;
my $even = 0;
for (my $index = 0; $index < #barcode; $index++) {
($index % 2 ? $even : $odd) += $barcode[$index];
}
This works by indexing over #barcode and taking the mod 2 of the index, ie dividing the index by 2 and taking the remainder, and if the remainder is 1 adding that element of #barcode to $even otherwise to $odd.
That looks strange until you remember that arrays are 0 based so your first number of the barcode is stored in $barcode[0] which is an even index.
chomp( my #barcode = //, <STDIN> ); presumably was supposed to have a split before the //?
#barcode will have all the characters in the line read, including the newline. The chomp will change the final element from a newline to an empty string.
Better to chomp first so you just have your digits in the array:
chomp(my $barcode = <STDIN>);
my #barcode = split //, $barcode;
Another Perl, if the string is of length 11 and contains only digits
$ perl -le ' $_="12345678911"; s/(.)(.)|(.)$/$odd+=$1+$3;$even+=$2/ge; print "odd=$odd; even=$even" '
odd=26; even=21
$
with different input
$ perl -le ' $_="12121212121"; s/(.)(.)|(.)$/$odd+=$1+$3;$even+=$2/ge; print "odd=$odd; even=$even" '
odd=6; even=10
$
I have a scenario where I take 2 very big binary strings (having 100 characters) and I need to add them.
The issue is that I am getting the answer in the form 2.000xxxxxxxxxxe+2, whereas I need the precise answer, as another 100 character long string.
chomp($str1=<STDIN>);
chomp($str2=<STDIN>);
print "Str 1 is $str1\n";
print "Str 2 is $str2\n";
$t = $str1 + $str2;
print "Sum is $t\n";
Sample Input
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
Sample Output
Str1 is
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
Str2 is
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
Sum is
2.0022022220202e+099
As already suggested, you can use Math::BigInt core module,
use Math::BigInt;
# chomp($str1=<STDIN>);
# chomp($str2=<STDIN>);
# print "Str 1 is $str1\n";
# print "Str 2 is $str2\n";
my $t = Math::BigInt->new("0b$str1") + Math::BigInt->new("0b$str2");
print $t->as_bin;
In order to perform arithmetic on your strings, Perl converts them to floating-point numbers, which are inherently imprecise. If you want to avoid that, use Math::BigInt as already suggested ... or roll your own.
######## WARNING/GUARANTEE: This is practically certain to be
# slower, buggier, less portable, and less secure than Math::BigInt.
# In fact, I planted a security hole just to prove a point. Enjoy.
use strict;
use warnings;
sub addition {
my ($int1, $int2) = #_;
my #int1 = reverse split //, $int1;
my #int2 = reverse split //, $int2;
my $len = scalar(#int1>#int2 ? #int1 : #int2);
my #result;
my $carry = 0;
for (my $i=0; $i < $len; ++$i)
{
$int1[$i] |= 0;
$int2[$i] |= 0;
my $sum = $carry + $int1[$i] + $int2[$i];
if ($sum >= 10)
{
$carry = int($sum / 10);
$sum %= 10;
}
push #result, $sum;
}
push #result, $carry if $carry;
return join ('', reverse #result);
}
For clarification, if I had a list of 8 elements, i would want to randomly pick 2. If I had a list of 20 elements, I would want to randomly pick 5. I would also like to assure (though not needed) that two elements don't touch, i.e. if possible not the 3 and then 4 element. Rather, 3 and 5 would be nicer.
The simplest solution:
Shuffle the list
select the 1st quarter.
Example implementation:
use List::Util qw/shuffle/;
my #nums = 1..20;
my #pick = (shuffle #nums)[0 .. 0.25 * $#nums];
say "#pick";
Example output: 10 2 18 3 19.
Your additional restriction “no neighboring numbers” actually makes this less random, and should be avoided if you want actual randomness. To avoid that two neighboring elements are included in the output, I would iteratively splice unwanted elements out of the list:
my #nums = 1..20;
my $size = 0.25 * #nums;
my #pick;
while (#pick < $size) {
my $i = int rand #nums;
push #pick, my $num = $nums[$i];
# check and remove neighbours
my $len = 1;
$len++ if $i < $#nums and $num + 1 == $nums[$i + 1];
$len++, $i-- if 0 < $i and $num - 1 == $nums[$i - 1];
splice #nums, $i, $len;
}
say "#pick";
use strict;
use warnings;
sub randsel {
my ($fact, $i, #r) = (1.0, 0);
while (#r * 4 < #_) {
if (not grep { $_ == $i } #r) {
$fact = 1.0;
# make $fact = 0.0 if you really don't want
# consecutive elements
$fact = 0.1 if grep { abs($i - $_) == 1 } #r;
push(#r, $i) if (rand() < 0.25 * $fact);
}
$i = ($i + 1) % #_;
}
return map { $_[$_] } sort { $a <=> $b } #r;
}
my #l;
$l[$_] = $_ for (0..19);
print join(" ", randsel(#l)), "\n";
Warning: Project Euler Problem 1 Spoiler
I recently discovered Project Euler and decided to try a few of the problems. The first problem was to sum the numbers from 0-999 that are multiples of 3 or 5.
My first, "java-like" solution was:
print threeAndFive(1000)."\n";
# Returns the sum of the numbers less than $max that are multiples of 3 or 5
sub threeAndFive
{
my $max = shift;
my $sum = 0;
for (my $i=; $i < $max; $i++)
{
$sum+=$i if (validate($i));
}
return $sum;
}
sub validate
{
my $num = shift;
if ($num % 3 == 0 || $num % 5 == 0)
{
return 1;
}
return undef;
}
I then rewrote it in a more perlish fashion:
print eval(join ('+', map {($_ % 3 == 0 || $_ % 5 == 0) ? $_ : ()} (1 .. 999)));
While this is obviously way more concise than the original code, I feel that it can probably be shorter or done in a better fashion. For example, in Python, one can do:
print sum([i for i in range(1,1000) if i%3==0 or i%5==0])
Are there more concise/better/clearer ways to do this? Or other equivalent ways that use different functions? I'm interested in learning as much perl as I can, so the more solutions, the merrier.
Thanks in advance.
The Straightforward Approach
To answer your question, List::Util provides sum.
use List::Util qw( sum );
Or you could write your own
sub sum { my $acc; $acc += $_ for #_; $acc }
Then you get:
say sum grep { $_ % 3 == 0 || $_ % 5 == 0 } 0..999;
Of course, that's an unoptimised approach.
The Optimised Approach
You can easily reduce the above to Ω(1) memory from Ω(N) by using a counting loop.
my $acc;
for (1..999) { $acc += $_ if $_ % 3 == 0 || $_ % 5 == 0; }
say $acc;
But that's far from the best, since the result can be obtained in Ω(1) time and memory!
This is done by adding the sum of the multiples of 3 to the sum of the multiples of 5, then subtracting the sum of the multiples of 15, because the sums of the multiples of $x can be calculated using
( sum 1..floor($n/$x) ) * $x # e.g. 3+6+9+... = (1+2+3+...)*3
which can take advantage of the formula
sum 1..$n = $n * ($n+1) * 0.5
Less concise, but faster:
sub sum1toN { my $N = int(shift); ($N * ($N+1)) / 2; }
my $N = 999;
print sum1toN($N/3)*3 + sum1toN($N/5)*5 - sum1toN($N/15)*15, "\n";
The sum1toN function computes the sum of integers from 1 to N.
Since:
3 + 6 + 9 + 12 ... + 999
Equals:
(1 + 2 + 3 + ... 333 ) * 3
We can computes sum of multiples of 3 using sum1toN(N/3) * 3. And the same applies to 5. Note that since we count the multiples of by 15 in both cases, a subtraction of sum1toN(N/15)*15 is needed.
I am facing some problem while adding values in numeric string:
I have string that looks like 02:03:05:07:04:06. All the numbers have to be <10. Now, I have to take a random number from 1-9 and add that number with last position number of the string (e.g. 3).
I the sum>10, then I have add that number to the number in the second last position.
So far, I have
#!/usr/bin/perl -w
use strict;
my $str='02:03:05:07:04:06';
my #arr=split(/:/,$str);
my #new_arr=pop(#arr);
my $rand_val=int(rand(9));
my $val=$new_arr[0]+$rand_val;
if($val>=10)
{
I am unable to generate a logic here:(
}
Please help me out of this problem.
After adding the number we have to join the string and print it also :)
my $str = '02:03:05:07:04:06';
my #nums = split /:/, $str;
my $add = int(rand(9)) + 1;
my $overflow = 1;
for (1..#nums) {
if ($num[-$_] + $add < 10) {
$num[-$_] += $add;
$overflow = 0;
last;
}
}
die "Overflow" if $overflow;
$str = join ':', map sprintf('%02d', $_), #nums;
I just run this and it works. The caveat is that, the lower the last number of the string is, the smaller the chance the "if ($val>=10)" will be valid
This doesn't solve the problem of your rand_val potentially being 0, but I'll leave that as a task for you to resolve. This should give you what you're looking for in terms of traversing through the values in the array until the the sum of the random value and current most-last value in the array.
1 use strict;
2 my $str='02:03:05:07:04:06';
3 my #arr=split(/:/,$str);
4 my $rand_val=int(rand(9));
5 my $val;
6
7 foreach my $i (reverse #arr){
8 $val = $i + $rand_val;
9 next if ($val >= 10);
10 print "val: $val, rand_val: $rand_val, value_used: $i\n";
11 last if ($val < 10);
12 }
I see a misstake : you do
my #new_arr=pop(#arr);
(...)
my $val=$new_arr[0]+$rand_val;
but pop only returns the last element, not a list.