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

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

Related

Is there a simple (possibly obfuscated) math expression for the number of days in a given month?

For use in cases where a standard library is not available. Assume that the month is given as an unsigned integer.
I'd be interested in seeing the shortest arithmetic expression that gives the correct answer, allowing or disallowing bitwise operators & masks but not lookup tables. Partial expressions can be saved into a variable for readability to showcase the idea used.
Here's an approach that uses only four simple arithmetic and bitwise ops and a 26-bit constant:
int days_in_month(unsigned m) {
// 121110 9 8 7 6 5 4 3 2 1 0
return 28 + ((0b11101110111110111011001100u >> m * 2u) & 0b11);
}
If you also want to handle leap year (no mention of it in the question), you can take a similar approach, at the cost of a few more operations and a 50-bit constant:
int days_in_month2(unsigned m, bool ly) {
return 28 + ((0b11101110111110111011011111101110111110111011001100u >> (m + 12*ly) * 2u) & 0b11);
}
If you are willing the pass the leap year in a different way, e.g., setting a bit like month | 16 to indicate leap year, it would be more efficient.
I assume you pass the month as 1 to 12, not 0 to 11.
Tests and generated asm can be seen on godbolt.
Variation om #BeeOnRope nice answer.
#include <stdbool.h>
int DaysPerMonth(int Month, bool IsLeapYear) {
assert(Month >= 1 && Month <= 12);
// 0b11101110111110111011001100u
// 3 B B E E C C
return (((0x3BBEECCu | (IsLeapYear << 2*2)) >> Month*2) & 3) + 28;
}
#include <stdio.h>
int main() {
for (int ly = 0; ly <= 1; ly++) {
for (int m = 1; m <= 12; m++) {
printf("(%2d %2d), ", m, DaysPerMonth(m,ly));
}
puts("");
}
return 0;
}
Brute force answer in pseudocode for readability:
monthlength(month,is_leapyear) :=
oddmonth = ( month + (month >= 8) ? 1 : 0) % 2 // Or (month ^ (month >> 3))&1
feb_days_offset = (month == 2) ? 2 - is_leapyear : 0
return 30 + oddmonth - feb_days_offset
Where month >= 8 can also be implemented with a bitshift since it's just the fourth bit in an unsigned representation, so that oddmonth is (first bit) xor (fourth bit), which can be consisely written as (month ^ (month >> 3))&1 . Similarly, subtracting the feb offset can be thought of as flipping the second bit on febuary, and flipping the first bit during leap year february.
single line with no intermediate variables:
monthlength(month,isleapyear) := 30 + ( month + (month >= 8 ? 1 : 0)) % 2 - (month==2 ? (2 - isleapyear) : 0)
Alternatively, one that uses exclusively bitwise arithmetic and shifts using the tricks discussed above:
monthlength(month,leapyear) := 30 ^ (month==2)<<1 ^ (month==2)&leapyear ^ (month^month>>3)&1
unsigned int m, leapyr, y ;
//m = month range is 1 to 12
//y = year range is 00 to 99
leapyr = ( ( y & 0x03 ) && 1 ); //0 means leap year and 1 means Non leap year
m = 30 + ( ( m & 1 ) ^ ( 1 && ( m & 8 ) ) ) - ( ( !( m & 13 ) ) ) - ( ( !( m & 13 ) ) & leapyr );
My answer is considering year. If you don't want to use assign leapyr variable 0 or 1 as you wish.

How can I use square brackets in perl like C?

Yesterday Computerphile uploaded a video about code golf and bitshift variations and I was really fascinated of the program generating music.
This is my formatted version of the code in the video.
int g(int sample, int x, int t, int overdrive) {
return (
(
3 & x & (
sample *
(
(
3 & sample >> 16
?
"BY}6YB6%"
:
"Qj}6jQ6%"
)[t % 8]
+
51
) >> overdrive
)
) << 4
);
}
int main(int n, int s) {
for (int sample=0 ;; sample++)
putchar(
g(sample, 1, n=sample >> 14, 12)
+
g(sample, s=sample >> 17, n^sample >> 13, 10)
+
g(sample, s/3, n + ((sample >> 11) % 3), 10)
+
g(sample, s/5, 8 + n-((sample >> 10) % 3), 9)
);
}
I wanted to try convert the program to pure perl and this is my attempt.
sub g {
return (
(
3 & $_[1] & (
$_[0] *
(
(
3 & $_[0] >> 16
?
"BY}6YB6%"
:
"Qj}6jQ6%"
)[$_[2] % 8]
+
51
) >> $_[3]
)
) << 4
);
}
for($i=0;;$i++){
print pack('C',
g($i, 1, $n=$i >> 14, 12)
+
g($i, $s=$i >> 17, $n^$i >> 13, 10)
+
g($i, $s/3, $n + (($i >> 11) % 3), 10)
+
g($i, $s/5, 8 + $n-(($i >> 10) % 3), 9)
);
}
According to the manual for putchar the the value is internally converted to an unsigned char when written. So I used the pack function in perl with C template which is an unsigned char. However when I pipe the result of both programs to aplay they don't produce the same music.
If I use inline C and call the function g from perl it does work correctly.
use Inline C => <<'END_C';
int g(int sample, int x, int t, int overdrive) {
return (
(
3 & x & (
sample *
(
(
3 & sample >> 16
?
"BY}6YB6%"
:
"Qj}6jQ6%"
)[t % 8]
+
51
) >> overdrive
)
) << 4
);
}
END_C
for($i=0;;$i++){
print pack('C',
g($i, 1, $n=$i >> 14, 12)
+
g($i, $s=$i >> 17, $n^$i >> 13, 10)
+
g($i, $s/3, $n + (($i >> 11) % 3), 10)
+
g($i, $s/5, 8 + $n-(($i >> 10) % 3), 9)
);
}
The only explanation I have is that [$_[2] % 8] is not doing what I think it is in perl.
How can I make the programs produce the same music in perl and C? On windows you can use perl theprogram.pl | sox -c 1 -b 8 -e unsigned -t raw -r 8k - -t waveaudio 0 if you have sox installed.
String is not an array in perl, you need to replace array access with call to substr() function:
...
ord(substr((
3 & $_[0] >> 16
?
"BY}6YB6%"
:
"Qj}6jQ6%"
), $_[2] % 8, 1))
...
More efficient:
# Outside the sub.
my $a1 = [ unpack 'C*', "BY}6YB6%" ];
my $a2 = [ unpack 'C*', "Qj}6jQ6%" ];
...
${ 3 & $_[0] >> 16 ? $a1 : $a2 }[ $_[2] % 8 ]
...

Escaping commas in macro output

I am trying to write a macro which enables me to transform
(a, b, c, d) to (a, a + b, a + b + c, a + b + c + d), etc. Here is what I have got so far:
macro_rules! pascal_next {
($x: expr) => ($x);
($x: expr, $y: expr) => (
($x, $x + $y)
);
($x: expr, $y: expr, $($rest: expr),+) => (
($x, pascal_next!(
$x + $y, $($rest),+
)
)
);
}
However, there is a problem that it would actually output (a, (a + b, (a + b + c, a + b + c +d))). The origin is that the second matching rule ($x: expr, $y: expr) => (($x, $x + $y));, produces an extra bracket, so that there would be nested brackets. If I don't put a bracket outside, I would get the error error:
unexpected token: ,
So is it possible to output a comma , in Rust macros?
No; the result of a macro must be a complete grammar construct like an expression or an item. You absolutely cannot have random bits of syntax like a comma or a closing brace.
You can get around this by simply not outputting anything until you have a complete, final expression. Behold!
#![feature(trace_macros)]
macro_rules! pascal_impl {
/*
The input to this macro takes the following form:
```ignore
(
// The current output accumulator.
($($out:tt)*);
// The current additive prefix.
$prefix:expr;
// The remaining, comma-terminated elements.
...
)
```
*/
/*
Termination condition: there is no input left. As
such, dump the output.
*/
(
$out:expr;
$_prefix:expr;
) => {
$out
};
/*
Otherwise, we have more to scrape!
*/
(
($($out:tt)*);
$prefix:expr;
$e:expr, $($rest:tt)*
) => {
pascal_impl!(
($($out)* $prefix+$e,);
$prefix+$e;
$($rest)*
)
};
}
macro_rules! pascal {
($($es:expr),+) => { pascal_impl!((); 0; $($es),+,) };
}
trace_macros!(true);
fn main() {
println!("{:?}", pascal!(1, 2, 3, 4));
}
Note: To use this on a stable compiler, you will need to delete the #![feature(trace_macros)] and trace_macros!(true); lines. Everything else should be fine.
What this does is it recursively munches away at the input, passing the partial (and potentially semantically invalid) output as input to the next level of recursion. This lets us build up an "open list", which we couldn't otherwise do.
Then, once we're out of input, we just re-interpret our partial output as a complete expression and... done.
The reason I including the tracing stuff is so I could show you what it looks like as it runs:
pascal! { 1 , 2 , 3 , 4 }
pascal_impl! { ( ) ; 0 ; 1 , 2 , 3 , 4 , }
pascal_impl! { ( 0 + 1 , ) ; 0 + 1 ; 2 , 3 , 4 , }
pascal_impl! { ( 0 + 1 , 0 + 1 + 2 , ) ; 0 + 1 + 2 ; 3 , 4 , }
pascal_impl! { ( 0 + 1 , 0 + 1 + 2 , 0 + 1 + 2 + 3 , ) ; 0 + 1 + 2 + 3 ; 4 , }
pascal_impl! { ( 0 + 1 , 0 + 1 + 2 , 0 + 1 + 2 + 3 , 0 + 1 + 2 + 3 + 4 , ) ; 0 + 1 + 2 + 3 + 4 ; }
And the output is:
(1, 3, 6, 10)
One thing to be aware of: large numbers of un-annotated integer literals can cause a dramatic increase in compile times. If this happens, you can solve it by simply annotating all of your integer literals (like 1i32).

Powershell is there an easy way to covert an int 5 to a string five or 68 to sixty eight?

I'm trying to figure out if there is an easy way to convert numbers into words take 9 and convert it to nine.
There is an excellent library for .NET called Humanizer that can do exactly this. I haven't tried this yet, but it looks like there is a PowerShell wrapper for it. I suspect this will do exactly what you need.
This has been asked about .NET/C#; you could put this in a class and use Add-Type in powershell to make this work.
.NET convert number to string representation (1 to one, 2 to two, etc...)
Maybe something like this (untested):
$class = #"
public class Num2Word
{
public static string NumberToText( int n)
{
if ( n < 0 )
return "Minus " + NumberToText(-n);
else if ( n == 0 )
return "";
else if ( n <= 19 )
return new string[] {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight",
"Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen",
"Seventeen", "Eighteen", "Nineteen"}[n-1] + " ";
else if ( n <= 99 )
return new string[] {"Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy",
"Eighty", "Ninety"}[n / 10 - 2] + " " + NumberToText(n % 10);
else if ( n <= 199 )
return "One Hundred " + NumberToText(n % 100);
else if ( n <= 999 )
return NumberToText(n / 100) + "Hundreds " + NumberToText(n % 100);
else if ( n <= 1999 )
return "One Thousand " + NumberToText(n % 1000);
else if ( n <= 999999 )
return NumberToText(n / 1000) + "Thousands " + NumberToText(n % 1000);
else if ( n <= 1999999 )
return "One Million " + NumberToText(n % 1000000);
else if ( n <= 999999999)
return NumberToText(n / 1000000) + "Millions " + NumberToText(n % 1000000);
else if ( n <= 1999999999 )
return "One Billion " + NumberToText(n % 1000000000);
else
return NumberToText(n / 1000000000) + "Billions " + NumberToText(n % 1000000000);
}
}
#"
Add-Type -TypeDefinition $class
[Num2Word]::NumberToText(555)
There's no reason you couldn't write this as pure powershell, but this was already written!

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.