Skip to content

Commit

Permalink
Improve performance of A6 algorithm and input code (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-shrimp authored Mar 22, 2022
1 parent 134acd7 commit c4609c6
Show file tree
Hide file tree
Showing 16 changed files with 513 additions and 747 deletions.
153 changes: 150 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"a4",
"a5",
"a6",
"a6/a6_benchgen",
"read_u32",
"test_run_bin",
]
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ The following procedures may require familiarity with the comamnd line.
1. [Install Git](https://git-scm.com/downloads) if it hasn't been installed.

2. Install Rust using `rustup` from
[Rust's official installation page](https://www.rust-lang.org/tools/install).
(There are some benchmarks that require nightly Rust to compile.
To use nightly Rust, choose the `nightly` channel when installing with `rustup`.)
[Rust's official installation page](https://www.rust-lang.org/tools/install).<br>
(Some code requires nightly Rust to compile.
To use nightly Rust, choose the `nightly` channel when installing with `rustup`.<br>
While we will try to use stabilized APIs only, we may include stabilized APIs
that have not entered the `stable` channel yet, so `nightly` is still required.)

3. Clone this repository:
```
Expand Down
8 changes: 4 additions & 4 deletions a5/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ fn main() {

// Input routine.
fn input_price_wallet() -> impl Iterator<Item = (u32, u32)> {
let reader = read_u32::U32Reader::new();
let mut reader = read_u32::U32Reader::with_stdin();

let test_case_count = reader.read_until(b'\n');
let test_case_count = reader.read_until_newline();

std::iter::repeat_with(move || {
let price = reader.read_until(b' ');
let wallet = reader.read_until(b'\n');
let price = reader.read_until_space();
let wallet = reader.read_until_newline();
(price, wallet)
})
.take(test_case_count as usize)
Expand Down
3 changes: 2 additions & 1 deletion a6/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ read_u32 = { path = "../read_u32" }
[dev-dependencies]
test_run_bin = { path = "../test_run_bin" }
criterion = "0.3"
primal = "0.3"
a6_benchgen = { path = "./a6_benchgen" }
rand = "0.8"

[[bench]]
name = "solve_bad_case"
Expand Down
15 changes: 15 additions & 0 deletions a6/a6_benchgen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "a6_benchgen"
version = "0.1.0"
authors = ["ivan-shrimp"]
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Input data generator for SPCC Kickstart Problem A6"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
primal = "0.3"
rand = "0.8"
anyhow = "1.0"
clap = { version = "3.1.6", features = ["derive"] }
47 changes: 47 additions & 0 deletions a6/a6_benchgen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! A generator of bad case data that most A6 solutions will likely take a long time to solve.
/// Maximum input value for Problem A6.
pub const A6_MAX: u32 = 1_000_000;
const SQRT_A6_MAX: u32 = 1_000;

/// Minimum input value for Problem A6.
pub const A6_MIN: u32 = 2;

/// Returns a set of bad case data for A6, which contains:
/// - All numbers with 2, 3, 7, 8, 12, 13, 17, 18, 22, 23, 27 or 28 total prime factors; and
/// - Primes above `A6_MAX / 4`. (4 is the smallest number in this set)
pub fn bad_case() -> impl Iterator<Item = u32> {
const WANTED_NUMBER_OF_PRIME_FACTORS: [u32; 12] = [2, 3, 7, 8, 12, 13, 17, 18, 22, 23, 27, 28];

let count_prime_factors = prime_counter();

(A6_MIN..=A6_MAX).filter(move |&n| {
let prime_factors = count_prime_factors(n);
WANTED_NUMBER_OF_PRIME_FACTORS.contains(&prime_factors)
|| (n > A6_MAX / 4 && prime_factors == 1)
})
}

/// Creates a function that counts the number of prime factors of any number below `A6_MAX`.
/// e.g.: `prime_counter()(6)` is 2, `prime_counter()(9)` is also 2.
/// This is known as the "big omega" variant of the "prime omega function", see
/// <https://en.wikipedia.org/wiki/Prime_omega_function>.
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
fn prime_counter() -> impl Fn(u32) -> u32 {
// A sieve to `sqrt(N)` can factorize numbers up to `N`.
let sieve = primal::Sieve::new(SQRT_A6_MAX as usize);

move |num: u32| {
sieve
// Factorize the number.
// Does not truncate as we're not running on 16-bit targets. We don't.
.factor(num as usize)
// The input, which should not be more than `A6_MAX`, can always be factorized by `sieve`.
.unwrap()
.into_iter()
// Sum up the exponents of each prime factor.
// Does not truncate because a number can't have more prime factors than itself.
.map(|(_, exp)| exp as u32)
.sum()
}
}
Loading

0 comments on commit c4609c6

Please sign in to comment.