Rust w/ Rayon -- 0.9s runtime, 6.7s cputime for whole range
(1/99) a=0 1 (best: 0)
(2/49) b=2 4 (best: 50)
(3/27) a=1 49 (best: 72)
(4/23) b=3 188 (best: 76)
(5/21) a=2 1176 (best: 78)
(6/17) b=4 4332 (best: 82)
(7/15) a=3 18424 (best: 84)
(8/15) b=5 65220 (best: 84)
(9/15) a=4 211876 (best: 84)
(10/15) b=6 721396 (best: 84)
(11/15) a=5 1906884 (best: 84)
(12/15) b=7 6249964 (best: 84)
(13/14) a=6 13983816 (best: 85)
(14/14) b=8 44156732 (best: 85)
B_max: 85
n_min: 933544295655093
n_max: 1125891114428899
Found values: 1121216655081291 1043134657624555 1120665056942829 1116514939453621 933544295655093 1125074802425067 1124798782678823 1125891114428899 1090147221387163
100 85 933544295655093 1125891114428899
________________________________________________________
Executed in 909.41 millis fish external
usr time 6.70 secs 0.00 micros 6.70 secs
sys time 0.02 secs 516.00 micros 0.02 secs
Output of time max_square 100
Tested on my laptop with 4 cores (8 virtual) @ ~4.5 GHz
use std::{str::FromStr, collections::HashSet};
use rayon::prelude::*;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU32, Ordering};
use rayon::iter;
fn solve(n: u32, verbose: bool) {
if n < 6 {
match n {
1 => {println!("1 1 1 1")},
2 => {println!("None")},
3 => {println!("3 1 2 2")},
4 => {println!("4 2 3 3")},
5 => {println!("5 3 5 5")},
_ => {println!("Error");}
}
return;
}
let mut best = Solution::new(n);
let mut a = 0;
let mut b = 2;
while best.get_best() + a + b <= n {
let cost_a = calculate_cost_a(n, a);
let cost_b = calculate_cost_b(n, b);
if cost_a < cost_b {
if verbose {eprintln!("({}/{}) a={} {} (best: {})", a+b-1, n - best.get_best() - 1, a, cost_a, best.get_best());}
step_a(n, a, &mut best);
a += 1;
} else {
if verbose {eprintln!("({}/{}) b={} {} (best: {})", a+b-1, n - best.get_best() - 1, b, cost_b, best.get_best());}
step_b(n, b, &mut best);
b += 1;
}
}
let solutions = best.solutions.lock().unwrap();
if verbose {
eprintln!("");
eprintln!("B_max: {}", best.get_best());
eprintln!("n_min: {}", solutions.iter().min().expect("No solutions"));
eprintln!("n_max: {}", solutions.iter().max().expect("No solutions"));
eprintln!("");
eprint!("Found values: ");
for z in solutions.iter() {
eprint!("{} ", z);
}
eprintln!("");
eprintln!("");
}
println!("{} {} {} {}", n, best.get_best(), solutions.iter().min().expect("No solutions"), solutions.iter().max().expect("No solutions"));
}
fn calculate_cost_a(n: u32, a: u32) -> i64 {
let a = a as i64;
let n = n as i64;
binomial((n-1)/2, a)
}
fn step_a(n: u32, a: u32, best: &Solution) {
let h = n / 2;
iter::split(
Combinator::with_prefix_postfix(n, a, 1, 1, (1 << h) - 1, h),
split_combinator).flatten_iter()
.for_each(|z|{
best.update(isqrt(z));
}
);
}
fn calculate_cost_b(n: u32, b: u32) -> i64 {
let b = b as i64;
let n = n as i64;
(1..b/2 + 1).map(|i| (1<<i) * binomial(n/2 - 1 - 2*i, b - 2*i)).sum::<i64>() * 2
}
fn step_b(n: u32, b: u32, best: &Solution) {
let m = (1 << ((n + 1) / 2)) - 1;
for k in 0..b/2 {
let w = ((n + 1) / 2) - 3 - 2*k;
iter::split(Combinator::new(w, b - 2 - 2*k, 1, 3), split_combinator).flatten_iter()
.for_each(|a| {
let x = modsqrt(w + 3, a) << k;
let mut z = 1 << ((n + 1) / 2);
let step_size = 1 << (((n + 1) / 2) - 1 - k);
while z > 0 {
z -= step_size;
best.update((z + x)&m);
best.update((z - x)&m);
}
}
);
}
}
fn modsqrt(n: u32, a: i128) -> i128 {
let mut x = 1;
let m = (1<<n) - 1;
for _ in 3..n {
x = (((x*x - a) >> 1) + x) & m;
}
x
}
fn isqrt(n: i128) -> i128 {
let mut x = (n as f64).sqrt() as i128;
if x*x <= n {
x += 1;
while x*x <= n {
x += 1;
}
x-1
} else {
x -= 1;
while x*x > n {
x -= 1;
}
x
}
}
fn binomial(n: i64, k: i64) -> i64 {
let k = std::cmp::min(k, n-k);
if k < 0 {return 0;}
if k == 0 {return 1;}
let mut c: i64 = 1;
for i in 0..(k as i64) {
c = (c * (n-i)) / (i + 1);
}
c
}
struct Combinator {
curr: i128,
mask: i128,
offset: u32,
min: i128,
max: i128,
k: u32,
}
impl Combinator {
fn new(width: u32, num_zeros: u32, mask: i128, offset: u32) -> Combinator {
Combinator { curr: (1 << width) - (1 << num_zeros), mask: mask, offset: offset, min: 0, max: binomial(width as i64, num_zeros as i64) as i128, k: width - num_zeros }
}
fn with_prefix_postfix(full_width: u32, num_zeros: u32, prefix: i128, prefix_width: u32, postfix: i128, postfix_width: u32) -> Combinator {
let width = full_width - prefix_width - postfix_width;
Combinator::new(width, num_zeros, (prefix << (width + postfix_width)) + postfix, postfix_width)
}
}
// calculates the next combination, in reverse lexicographic order
fn next_combination(n: i128) -> i128 {
if n&1 == 0 {return n - (((n^(n-1))+1)>>2);}
let a = (n^(n+1))>>1;
let na = n-a;
if na == 0 {return na;}
let b = ((na ^ (na-1))>>2) - a;
if b == 0 {return na - 1;}
let b = b >> b.trailing_zeros();
na - 1 - b
}
impl Iterator for Combinator {
type Item = i128;
fn next(&mut self) -> Option<Self::Item> {
if self.min == self.max {return None;}
let curr = self.curr;
self.max -= 1;
self.curr = next_combination(curr);
Some(self.mask | (curr << self.offset))
}
}
// combinatorial number system
fn ntoc(n: i128, k: i128) -> i128 {
if k == 0 {return 0;}
if n == 0 {return (1<<k) - 1;}
let mut x = k;
let mut xck = 1; // x choose k
let mut x1ck = k+1; // x+1 choose k
while x1ck <= n {
xck = x1ck;
x += 1;
x1ck = (xck * (x + 1)) / (x + 1 - k);
}
ntoc(n - xck, k-1) + (1 << x)
}
fn split_combinator(c: Combinator) -> (Combinator, Option<Combinator>) {
if c.max - c.min <= 100000 {return (c, None);}
let midpoint = c.min + (c.max - c.min) / 2;
(Combinator {curr: ntoc(midpoint, c.k as i128), min: c.min, max: midpoint, mask: c.mask, offset: c.offset, k: c.k}, Some(Combinator {curr: c.curr, min: midpoint, max: c.max, mask: c.mask, offset: c.offset, k: c.k}))
}
struct Solution {
best: AtomicU32,
minz: i128,
maxz: i128,
solutions: Arc<Mutex<HashSet<i128>>>,
}
impl Solution {
fn update(&self, z: i128) {
if z < self.minz {return;}
if z > self.maxz {return;}
let z2 = z*z;
let z2c = z2.count_ones();
let prev = self.best.fetch_max(z2c, Ordering::Relaxed);
if z2c < prev {return;}
let mut solutions = self.solutions.lock().unwrap();
if z2c > prev {
solutions.clear();
}
solutions.insert(z);
}
fn new(width: u32) -> Solution {
Solution {best: AtomicU32::new(0), minz: isqrt((1 << (width - 1)) - 1) + 1, maxz: isqrt(1 << width), solutions: Arc::new(Mutex::new(HashSet::new()))}
}
fn get_best(&self) -> u32 {
self.best.load(Ordering::Relaxed)
}
}
fn main() {
let args: Vec<String> = std::env::args().collect();
let i = match args.len() {
2 => (1, true),
3 => {
if args[1] == "-q" {
(2, false)
} else if args[2] == "-q" {
(1, false)
} else {
println!("Usage: max_square [-q] [number of bits]");
return;
}
},
_ => {
println!("Usage: max_square [-q] [number of bits]");
return;
},
};
if let Ok(n) = u32::from_str(&args[i.0]) {
solve(n, i.1);
} else {
println!("Error reading args");
}
}
Explanation
We'll let \$n\$ be the number of bits (100) in the squares we're searching.
This algorithm has two types of steps that corresponding to fixing the most significant / least significant half of the square.
While there are possible solutions remaining, we chose between the two step types based on which would require fewer operations.
Step type \$a\$:
Range over all integers \$x\$ with \$\left\lceil\frac{n}{2}\right\rceil-1\$ bits of which \$a\$ are zero.
For each \$x\$ there is at most one natural number \$z\$ for which
$$
\underbrace{1x0\ldots0}_{n\text{ bits}}
\le
z^2
\le
\underbrace{1x1\ldots1}_{n\text{ bits}}
$$
After updating our solutions with those \$z^2\$, assuming we already checked the previous values of \$a\$, we now know that all unchecked squares have at least \$a+1\$ zeros in the first half.
Step type \$b\$:
Range over integers \$k\$ and \$x\$ where \$2+2k\le b\$ and \$x\$ has \$\left\lceil\frac{n}{2}\right\rceil-3-2k\$ bits of which \$b-2-2k\$ are zero.
For each \$x\$, \$k\$ there are \$4\cdot2^k\$ values of \$z\$ (mod \$2^{\left\lceil\frac{n}{2}\right\rceil}\$) satisfying
$$
z^2\equiv \underbrace{x001[00]\times k}_{\left\lceil\frac{n}{2}\right\rceil\text{ bits}}\mod2^{\left\lceil\frac{n}{2}\right\rceil}
$$
After updating our solutions with those \$z^2\$, assuming we already checked the previous values of \$b\$, we now know that all unchecked squares have at least \$b+1\$ zeros in the first half.
Output for n from 3 to 126
3 1 2 2
4 2 3 3
5 3 5 5
6 3 7 7
7 5 11 11
8 4 13 15
9 6 21 21
10 6 27 27
11 8 45 45
12 8 53 53
13 9 75 89
14 9 101 117
15 13 181 181
16 11 217 235
17 13 362 362
18 12 437 491
19 14 627 723
20 15 923 949
21 16 1241 1241
22 16 1619 1773
23 17 2505 2891
24 18 3915 3915
25 19 5221 5747
26 20 6475 7093
27 21 11309 11309
28 22 15595 16203
29 23 19637 19637
30 24 31595 31595
31 25 44491 44491
32 25 61029 64747
33 26 69451 86581
34 27 113447 113447
35 28 185269 185269
36 29 244661 244661
37 30 357081 357081
38 31 453677 453677
39 31 642119 738539
40 34 980853 980853
41 33 1380917 1481453
42 34 1961706 2079669
43 37 2965685 2965685
44 37 3923411 3923411
45 38 5931189 5931189
46 38 8096813 8222581
47 39 11862197 11862965
48 39 16244453 16244453
49 41 22193965 22193965
50 41 32988235 32988235
51 42 43586515 47450235
52 44 57804981 57804981
53 44 94905541 94905541
54 44 115609962 134053275
55 46 164382389 177549685
56 47 268123979 268123979
57 47 320528203 379526189
58 49 528407335 528407335
59 50 657506485 657506485
60 51 888717493 888717493
61 52 1510322221 1510322221
62 52 2100772683 2111529939
63 53 2630119323 2630119323
64 54 4227325771 4227325771
65 55 5853487285 5853487285
66 55 8580481947 8580481947
67 57 10520476455 10520476455
68 57 14878203147 17110519093
69 58 21040953067 24295633269
70 59 31469977781 31469977781
71 62 48589783221 48589783221
72 63 66537313397 66537313397
73 62 97179566261 97179566442
74 63 133074626794 133074626794
75 64 194366549037 194366549037
76 64 259365748555 274835944267
77 66 388730126517 388730126517
78 66 475016392885 540779528373
79 68 759031429451 759031429451
80 68 1064500084555 1081632748507
81 68 1503999171957 1554929409435
82 69 2055848542027 2197923974325
83 70 2576736968885 3109840793419
84 72 3646667521867 4362198168395
85 72 5384675514549 6216643955893
86 73 8445509046091 8791729601637
87 74 11620627528805 11937419436213
88 76 17435796250805 17435796250805
89 76 21537910687315 24879106612889
90 77 34895284158283 34895284158283
91 77 39275570260917 49752141391123
92 78 60940798018325 68134217006923
93 80 93089098383179 93089098383179
94 80 121861586862261 139768858984629
95 81 184920521899109 199030966469849
96 82 222524145495085 222524145495085
97 85 398064946368587 398064946368587
98 84 461438710099403 562672855105355
99 87 796095014224053 796095014224053
100 85 933544295655093 1125891114428899
101 88 1592054863872923 1592054863872923
102 88 2233021208375059 2233021208375059
103 89 3153273599102795 3153273599102795
104 90 4498915668727861 4498915668727861
105 92 6354993066190667 6354993066190667
106 91 7790298568288075 8721180941868443
107 92 12709986132381334 12711635715085643
108 93 18005548694652121 18005548694652121
109 94 25469208479569437 25469208479569437
110 94 30874441840049387 36004547995030069
111 95 47233732013276085 50801164553247509
112 96 66646890995961483 72057405025529767
113 99 101480601046523531 101480601046523531
114 98 143542302959229109 143542302959229109
115 99 202961202093047062 202961202093047062
116 101 269614819660724085 269614819660724085
117 101 403417952942237229 403417952942237229
118 101 529472308325235947 576458535517221045
119 103 808844333869390923 808844333869390923
120 103 970104967079909557 1143803445656875189
121 104 1578696363991412555 1630401033738048053
122 106 1996776422069106507 2156918025961517429
123 106 2688433943050910795 3260133329992761163
124 107 4575515161264941861 4605489388616186187
125 108 6518622390190614475 6518622390190614475
126 110 7977185712837244873 7977185712837244873
________________________________________________________
Executed in 498.22 secs fish external
usr time 65.63 mins 15.29 millis 65.63 mins
sys time 0.03 mins 19.41 millis 0.03 mins