shootout-fannkuch-redux rewrite

Less bound checking and parallelisation.  Brute speed improvement
is about 15% faster.
This commit is contained in:
Guillaume Pinot 2014-04-20 01:32:54 +02:00
parent d1d8497e53
commit 57d693460b

View file

@ -8,94 +8,65 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::os;
use std::cmp::max;
fn max(a: i32, b: i32) -> i32 {
if a > b {
a
} else {
b
}
fn fact(n: uint) -> uint {
range(1, n + 1).fold(1, |accu, i| accu * i)
}
fn fannkuch_redux(n: i32) -> i32 {
let mut perm = Vec::from_elem(n as uint, 0i32);
let mut perm1 = Vec::from_fn(n as uint, |i| i as i32);
let mut count = Vec::from_elem(n as uint, 0i32);
let mut max_flips_count = 0i32;
let mut perm_count = 0i32;
let mut checksum = 0i32;
fn fannkuch(n: uint, i: uint) -> (int, int) {
let mut perm = Vec::from_fn(n, |e| ((n + e - i) % n + 1) as i32);
let mut tperm = perm.clone();
let mut count = Vec::from_elem(n, 0u);
let mut perm_count = 0;
let mut checksum = 0;
let perm = perm.as_mut_slice();
let perm1 = perm1.as_mut_slice();
let count = count.as_mut_slice();
for countdown in range(1, fact(n - 1) + 1).rev() {
for i in range(1, n) {
let perm0 = *perm.get(0);
for j in range(0, i) {
*perm.get_mut(j) = *perm.get(j + 1);
}
*perm.get_mut(i) = perm0;
let mut r = n;
loop {
while r != 1 {
count[r as uint - 1] = r;
r -= 1;
}
for (perm_i, perm1_i) in perm.mut_iter().zip(perm1.iter()) {
*perm_i = *perm1_i;
}
let mut flips_count: i32 = 0;
let mut k: i32;
loop {
k = perm[0];
if k == 0 {
let count_i = count.get_mut(i);
if *count_i >= i {
*count_i = 0;
} else {
*count_i += 1;
break;
}
}
let k2 = (k+1) >> 1;
for i in range(0i32, k2) {
perm.swap(i as uint, (k - i) as uint);
}
tperm.clone_from(&perm);
let mut flips_count = 0;
loop {
let k = *tperm.get(0);
if k == 1 { break; }
tperm.mut_slice_to(k as uint).reverse();
flips_count += 1;
}
max_flips_count = max(max_flips_count, flips_count);
checksum += if perm_count % 2 == 0 {
flips_count
} else {
-flips_count
};
// Use incremental change to generate another permutation.
loop {
if r == n {
println!("{}", checksum);
return max_flips_count;
}
let perm0 = perm1[0];
let mut i: i32 = 0;
while i < r {
let j = i + 1;
perm1[i as uint] = perm1[j as uint];
i = j;
}
perm1[r as uint] = perm0;
count[r as uint] -= 1;
if count[r as uint] > 0 {
break;
}
r += 1;
}
perm_count += 1;
perm_count = max(perm_count, flips_count);
checksum += if countdown & 1 == 1 {flips_count} else {-flips_count}
}
(checksum, perm_count)
}
fn main() {
let args = os::args();
let n = if args.len() > 1 {
from_str::<i32>(args[1]).unwrap()
} else {
2
};
println!("Pfannkuchen({}) = {}", n as int, fannkuch_redux(n) as int);
let n = std::os::args().get(1).and_then(|arg| from_str(*arg)).unwrap_or(2u);
let (tx, rx) = channel();
for i in range(0, n) {
let tx = tx.clone();
spawn(proc() tx.send(fannkuch(n, i)));
}
drop(tx);
let mut checksum = 0;
let mut perm = 0;
for (cur_cks, cur_perm) in rx.iter() {
checksum += cur_cks;
perm = max(perm, cur_perm);
}
println!("{}\nPfannkuchen({}) = {}", checksum, n, perm);
}