How do I partially sort a Vec or slice? - performance

I need to get the top N items from a Vec which is quite large in production. Currently I do it like this inefficient way:
let mut v = vec![6, 4, 3, 7, 2, 1, 5];
v.sort_unstable();
v = v[0..3].to_vec();
In C++, I'd use std::partial_sort, but I can't find an equivalent in the Rust docs.
Am I just overlooking it, or does it not exist (yet)?

The standard library doesn't contain this functionality, but it looks like the lazysort crate is exactly what you need:
So what's the point of lazy sorting? As per the linked blog post, they're useful when you do not need or intend to need every value; for example you may only need the first 1,000 ordered values from a larger set.
#![feature(test)]
extern crate lazysort;
extern crate rand;
extern crate test;
use std::cmp::Ordering;
trait SortLazy<T> {
fn sort_lazy<F>(&mut self, cmp: F, n: usize)
where
F: Fn(&T, &T) -> Ordering;
unsafe fn sort_lazy_fast<F>(&mut self, cmp: F, n: usize)
where
F: Fn(&T, &T) -> Ordering;
}
impl<T> SortLazy<T> for [T] {
fn sort_lazy<F>(&mut self, cmp: F, n: usize)
where
F: Fn(&T, &T) -> Ordering,
{
fn sort_lazy<F, T>(data: &mut [T], accu: &mut usize, cmp: &F, n: usize)
where
F: Fn(&T, &T) -> Ordering,
{
if !data.is_empty() && *accu < n {
let mut pivot = 1;
let mut lower = 0;
let mut upper = data.len();
while pivot < upper {
match cmp(&data[pivot], &data[lower]) {
Ordering::Less => {
data.swap(pivot, lower);
lower += 1;
pivot += 1;
}
Ordering::Greater => {
upper -= 1;
data.swap(pivot, upper);
}
Ordering::Equal => pivot += 1,
}
}
sort_lazy(&mut data[..lower], accu, cmp, n);
sort_lazy(&mut data[upper..], accu, cmp, n);
} else {
*accu += 1;
}
}
sort_lazy(self, &mut 0, &cmp, n);
}
unsafe fn sort_lazy_fast<F>(&mut self, cmp: F, n: usize)
where
F: Fn(&T, &T) -> Ordering,
{
fn sort_lazy<F, T>(data: &mut [T], accu: &mut usize, cmp: &F, n: usize)
where
F: Fn(&T, &T) -> Ordering,
{
if !data.is_empty() && *accu < n {
unsafe {
use std::mem::swap;
let mut pivot = 1;
let mut lower = 0;
let mut upper = data.len();
while pivot < upper {
match cmp(data.get_unchecked(pivot), data.get_unchecked(lower)) {
Ordering::Less => {
swap(
&mut *(data.get_unchecked_mut(pivot) as *mut T),
&mut *(data.get_unchecked_mut(lower) as *mut T),
);
lower += 1;
pivot += 1;
}
Ordering::Greater => {
upper -= 1;
swap(
&mut *(data.get_unchecked_mut(pivot) as *mut T),
&mut *(data.get_unchecked_mut(upper) as *mut T),
);
}
Ordering::Equal => pivot += 1,
}
}
sort_lazy(&mut data[..lower], accu, cmp, n);
sort_lazy(&mut data[upper..], accu, cmp, n);
}
} else {
*accu += 1;
}
}
sort_lazy(self, &mut 0, &cmp, n);
}
}
#[cfg(test)]
mod tests {
use test::Bencher;
use lazysort::Sorted;
use std::collections::BinaryHeap;
use SortLazy;
use rand::{thread_rng, Rng};
const SIZE_VEC: usize = 100_000;
const N: usize = 42;
#[bench]
fn sort(b: &mut Bencher) {
b.iter(|| {
let mut rng = thread_rng();
let mut v: Vec<i32> = std::iter::repeat_with(|| rng.gen())
.take(SIZE_VEC)
.collect();
v.sort_unstable();
})
}
#[bench]
fn lazysort(b: &mut Bencher) {
b.iter(|| {
let mut rng = thread_rng();
let v: Vec<i32> = std::iter::repeat_with(|| rng.gen())
.take(SIZE_VEC)
.collect();
let _: Vec<_> = v.iter().sorted().take(N).collect();
})
}
#[bench]
fn lazysort_in_place(b: &mut Bencher) {
b.iter(|| {
let mut rng = thread_rng();
let mut v: Vec<i32> = std::iter::repeat_with(|| rng.gen())
.take(SIZE_VEC)
.collect();
v.sort_lazy(i32::cmp, N);
})
}
#[bench]
fn lazysort_in_place_fast(b: &mut Bencher) {
b.iter(|| {
let mut rng = thread_rng();
let mut v: Vec<i32> = std::iter::repeat_with(|| rng.gen())
.take(SIZE_VEC)
.collect();
unsafe { v.sort_lazy_fast(i32::cmp, N) };
})
}
#[bench]
fn binaryheap(b: &mut Bencher) {
b.iter(|| {
let mut rng = thread_rng();
let v: Vec<i32> = std::iter::repeat_with(|| rng.gen())
.take(SIZE_VEC)
.collect();
let mut iter = v.iter();
let mut heap: BinaryHeap<_> = iter.by_ref().take(N).collect();
for i in iter {
heap.push(i);
heap.pop();
}
let _ = heap.into_sorted_vec();
})
}
}
running 5 tests
test tests::binaryheap ... bench: 3,283,938 ns/iter (+/- 413,805)
test tests::lazysort ... bench: 1,669,229 ns/iter (+/- 505,528)
test tests::lazysort_in_place ... bench: 1,781,007 ns/iter (+/- 443,472)
test tests::lazysort_in_place_fast ... bench: 1,652,103 ns/iter (+/- 691,847)
test tests::sort ... bench: 5,600,513 ns/iter (+/- 711,927)
test result: ok. 0 passed; 0 failed; 0 ignored; 5 measured; 0 filtered out
This code allows us to see that lazysort is faster than the solution with BinaryHeap. We can also see that BinaryHeap solution gets worse when N increases.
The problem with lazysort is that it creates a second Vec<_>. A "better" solution would be to implement the partial sort in-place. I provided an example of such an implementation.
Keep in mind that all these solutions come with overhead. When N is about SIZE_VEC / 3, the classic sort wins.
You could submit an RFC/issue to ask about adding this feature to the standard library.

There is a select_nth_unstable, the equivalent of std::nth_element. The result of this can then be sorted to achieve what you want.
Example:
let mut v = vec![6, 4, 3, 7, 2, 1, 5];
let top_three = v.select_nth_unstable(3).0;
top_three.sort();
3 here is the index of the "nth" element, so we're actually picking the 4th element, that's because select_nth_unstable returns a tuple of
a slice to the left of the nth element
a reference to the nth element
a slice to the right of the nth element

Related

Sudoku generation backtracking creating incorrect boards

I am fairly new to Rust, creating this sudoku generator as one of my first projects. I am following algorithm - How to generate Sudoku boards with unique solutions, but it keeps generating boards that contain multiple of the same number per 3X3 area.
Here's the code I have for generating the boards:
use std::{fmt::{Debug, Formatter, Error, Display}, ops::Deref};
use rand::Rng;
use termion::color;
const POSITIONS: [i32; 10] = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
0
];
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Slot {
pub val: i32
}
#[derive(Clone, Debug)]
pub struct SlotGrid {
pub data: Vec<Vec<Slot>>
}
pub struct Board {
data: SlotGrid,
solved: SlotGrid,
pub game_data: SlotGrid
}
impl Slot {
pub fn new(val: i32) -> Self {
if !POSITIONS.to_vec().contains(&val) {
panic!("Invalid value");
}
Self { val: val }
}
}
// TODO: custom size support
impl SlotGrid {
pub fn new() -> Self {
let d1: Vec<Vec<i32>> = vec![
vec![1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![4, 5, 6, 7, 8, 9, 1, 2, 3],
vec![7, 8, 9, 1, 2, 3, 4, 5, 6],
vec![2, 3, 1, 5, 6, 4, 8, 9, 7],
vec![5, 6, 4, 8, 9, 7, 2, 3, 1],
vec![8, 9, 7, 2, 3, 1, 5, 6, 4],
vec![3, 1, 2, 6, 4, 5, 9, 7, 8],
vec![6, 4, 5, 9, 7, 8, 3, 1, 2],
vec![9, 7, 8, 3, 1, 2, 6, 4, 5]
];
let mut d2: Vec<Vec<Slot>> = Vec::new();
for row in d1 {
let mut row2: Vec<Slot> = Vec::new();
for item in row {
row2.push(Slot::new(item));
}
d2.push(row2);
}
Self { data: d2 }
}
pub fn set(&mut self, x: usize, y: usize, val: i32) {
self.data[y][x].val = val;
}
pub fn get(&self, x: usize, y: usize) -> Slot {
self.data[y][x]
}
fn swap_slots(&mut self, n1: i32, n2: i32) {
for y in 0 .. 8 {
for x in 0 .. 8 {
if self.get(x, y).val == n1 {
self.set(x, y, n2);
continue;
}
if self.get(x, y).val == n2 {
self.set(x, y, n1);
}
}
}
}
fn swap_rows(&mut self, y1: usize, y2: usize) {
let first: Vec<Slot> = self.data[y1].clone();
let second: Vec<Slot> = self.data[y2].clone();
self.data[y2] = first;
self.data[y1] = second;
}
fn swap_cols(&mut self, x1: usize, x2: usize) {
for y in 0 .. 8 {
let first = self.data[y][x1];
let second = self.data[y][x2];
self.data[y][x1].val = second.val;
self.data[y][x2].val = first.val;
}
}
fn swap_3x3_rows(&mut self, y1: usize, y2: usize) {
for i in 0 .. 2 {
self.swap_rows(y1 * 3 + i, y2 * 3 + i);
}
}
fn swap_3x3_cols(&mut self, x1: usize, x2:usize) {
for i in 0 .. 2 {
self.swap_cols(x1 * 3 + i, x2 * 3 + i);
}
}
pub fn shuffle_rows(&mut self) {
let mut rng = rand::thread_rng();
let mut num: usize;
for i in 0 .. 8 {
let rand = rng.gen::<usize>() % 3;
num = i / 3;
self.swap_rows(i, num * 3 + rand);
}
}
pub fn shuffle_cols(&mut self) {
let mut rng = rand::thread_rng();
let mut num: usize;
for i in 0 .. 0 {
let rand = rng.gen::<usize>() % 3;
num = i / 3;
self.swap_cols(i, num * 3 + rand);
}
}
pub fn shuffle_nums(&mut self) {
let mut rng = rand::thread_rng();
for i in 0 .. 8 {
let num = rng.gen::<usize>() % 9;
self.swap_slots(i, num.try_into().unwrap());
}
}
pub fn shuffle_3x3_rows(&mut self) {
let mut rng = rand::thread_rng();
for i in 0 .. 2 {
let num = rng.gen::<usize>() % 3;
self.swap_3x3_rows(i, num);
}
}
pub fn shuffle_3x3_cols(&mut self) {
let mut rng = rand::thread_rng();
for i in 0 .. 2 {
let num = rng.gen::<usize>() % 3;
self.swap_3x3_cols(i, num);
}
}
}
impl Board {
pub fn new(cmplx: usize) -> Self {
let mut data = SlotGrid::new();
let mut rng = rand::thread_rng();
data.shuffle_rows();
data.shuffle_cols();
data.shuffle_3x3_rows();
data.shuffle_3x3_cols();
let old_data = data.clone();
let mut out: SlotGrid = data.clone();
let mut i = 0;
loop {
// remove until usolvable and go back 1
let x = rng.gen::<usize>() % 9;
let y = rng.gen::<usize>() % 9;
data.set(x, y, 0);
if !back_track(&mut (data.clone()), 0, 0) && i >= cmplx {
break;
}
i += 1;
out = data.clone();
}
Self { data: out.clone(), solved: old_data, game_data: out }
}
pub fn is_solved(&self) -> bool {
for i in 0 .. self.data.data.len() - 1 {
for j in 0 .. self.data.data[i].len() - 1 {
if self.data.data[i][j] != self.solved.data[i][j] {
return false;
}
}
}
true
}
}
impl Display for Board {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let mut s = String::new();
let mut i = 10;
for row in &self.data.data {
i -= 1;
s.push_str(color::Fg(color::Magenta).to_string().deref());
s.push_str(i.to_string().deref());
s.push_str(" | ");
s.push_str(color::Fg(color::Reset).to_string().deref());
let mut j = 0;
for slot in row {
j += 1;
let val = if slot.val == 0 {
"_".to_string()
} else {
slot.val.to_string()
};
s.push_str(colorify(j, 9 - i, val).deref());
s.push(' ');
}
s.push_str("\n");
print!("\n");
}
s.push_str(color::Fg(color::Red).to_string().deref());
s.push_str(" A B C D E F G H I");
s.push_str(color::Fg(color::Reset).to_string().deref());
write!(f, "{}", s)
}
}
fn colorify(x: usize, y: usize, s: String) -> String {
let x2 = x - 1;
let corners: String = if (0 .. 3).contains(&x2) || (6 .. 9).contains(&x2) {
color::Fg(color::LightCyan).to_string()
} else {
color::Fg(color::Green).to_string()
};
let mut out = String::new();
if (0 .. 3).contains(&y) {
out.push_str(corners.deref());
}
else if (3 .. 6).contains(&y) {
if (3 .. 6).contains(&x2) {
out.push_str(color::Fg(color::LightCyan).to_string().deref());
}
else {
out.push_str(color::Fg(color::Green).to_string().deref());
}
}
else if (6 .. 9).contains(&y) {
out.push_str(corners.deref());
}
out.push_str(s.deref());
out.push_str(color::Fg(color::Reset).to_string().deref());
out
}
fn back_track(data: &mut SlotGrid, mut x: usize, mut y: usize) -> bool {
if x == 8 && y == 8 {
return true;
}
if x == 8 {
y += 1;
x = 0;
}
if data.get(x, y).val > 0 {
return back_track(data, x + 1, y);
}
for i in 1 .. 8 {
if is_safe(&data, x, y, i) {
data.set(x, y, i.try_into().unwrap());
if back_track(data, x + 1, y) {
return true;
}
}
data.set(x, y, 0);
}
return false;
}
pub fn is_safe(data: &SlotGrid, x: usize, y: usize, val: usize) -> bool {
println!("ran is_safe");
for y2 in 0 .. 8 {
if data.get(x, y2).val == val.try_into().unwrap() {
drop(data);
println!("A");
return false;
}
}
for x2 in 0 .. 8 {
if data.get(x2, y).val == val.try_into().unwrap() {
drop(data);
println!("B");
return false;
}
}
let start_row = y - (y % 3);
let start_col = x - (x % 3);
for i in 0 .. 2 {
for j in 0 .. 2 {
if data.get(i + start_row, j + start_col).val == val.try_into().unwrap() {
drop(data);
println!("C");
return false;
}
}
}
drop(data);
return true;
}
It appears that there may be something wrong with is_safe, but having multiple numbers per area means there is something wrong with the way the numbers are originally mixed. I have looked around a bit but have found nothing out of the ordinary (although I might just be blind). Can someone help me figure out what I did wrong here?
Like #harold said, the solution was to use inclusive operators in my code. There are still some errors in my main file to fix, but this specific issue is solved so I will be closing the question.

Rust error expected type `()` found type `(i32, i32)`

Pretty new to Rust, decided to brush up using the Advent of Code 2020 Day 1 Puzzle. I'm using the following function:
fn find_numbers_2020(v: Vec<i32>) -> (i32,i32) {
let mut n1: i32 = 0;
let mut n2: i32 = 0;
let mut cnt = 0;
let size = v.len();
for v_i in v {
n1 = v_i;
cnt = cnt+1;
for i in cnt..size {
if (n1 + *v.get(i).unwrap()) == 2020 {
n2 = *v.get(i).unwrap();
(n1, n2) //Issue is here
}
}
}
(n1, n2)
}
But I get the error
"32 (n1, n2)
^^^^^^^^ expected (), found tuple.
It's being called from main as follows
fn main() {
let filename = String::from("./input.txt");
let v = parse_file(filename); //This works fine
for v_i in v {
println!("{}", v_i);
}
let result = find_numbers_2020(v);
let (n1, n2) = result;
println!("{} + {} = {}", n1, n2, n1+n2);
println!("{} * {} = {}", n1, n2, n1*n2);
}
I should also mention that v is a Vec<i32>. Sorry for the beginner question but Rust can be a little confusing and I haven't been able to find any answers through googling.
You can omit the return keyword only if the returned value is the last expression in the function block, otherwise you need to explicitly use return. Adding return to your example fixes that particular error but a bunch of new ones come up in its place. This is how I'd write the function:
fn find_numbers_2020(v: Vec<i32>) -> (i32, i32) {
for (skip, &n1) in v.iter().enumerate() {
for &n2 in v.iter().skip(skip) {
if n1 + n2 == 2020 {
return (n1, n2);
}
}
}
panic!("no pair of numbers in vec sum to 2020");
}
This is one of those cases where you need to use a return expression. Your initial for loop also consumes v, so to be able to do v.get() in the inner loop, you need to borrow v instead, i.e. &v or v.iter().
fn find_numbers_2020(v: Vec<i32>) -> (i32, i32) {
let mut n1: i32 = 0;
let mut n2: i32 = 0;
let mut cnt = 0;
let size = v.len();
for &v_i in &v {
n1 = v_i;
cnt = cnt + 1;
for i in cnt..size {
if (n1 + *v.get(i).unwrap()) == 2020 {
n2 = *v.get(i).unwrap();
return (n1, n2);
}
}
}
(n1, n2)
}
This is also a perfect case of when you could return an Option and use an if let expression. So instead of returning (i32, i32) then you would return Option<(i32, i32)>.
Instead of doing *v.get(i).unwrap(), then you can also just index v, i.e. v[i].
fn find_numbers_2020(v: Vec<i32>) -> Option<(i32, i32)> {
let mut cnt = 0;
for &n1 in &v {
cnt += 1;
for i in cnt..v.len() {
if (n1 + v[i]) == 2020 {
let n2 = v[i];
return Some((n1, n2));
}
}
}
None
}
fn main() {
// ...
if let Some((n1, n2)) = result {
println!("{} + {} = {}", n1, n2, n1 + n2);
println!("{} * {} = {}", n1, n2, n1 * n2);
}
}
Instead of manually incrementing indices, you can use enumerate() in the first loop, then in the second loop you can use skip(i + 1) with the index returned by enumerate().
fn find_numbers_2020(v: Vec<i32>) -> Option<(i32, i32)> {
for (i, &n1) in v.iter().enumerate() {
for &n2 in v.iter().skip(i + 1) {
if (n1 + n2) == 2020 {
return Some((n1, n2));
}
}
}
None
}
Instead of v: Vec<i32>, it would be more idiomatic to use a slice, i.e. v: &[i32]. In main() you just have to borrow v in your find_numbers_2020() call, i.e. find_numbers_2020(&v)

Why can't I mutably borrow a primitive from an enum?

I would like to be able to obtain references (both immutable and mutable) to the usize wrapped in Bar in the Foo enum:
use Foo::*;
#[derive(Debug, PartialEq, Clone)]
pub enum Foo {
Bar(usize)
}
impl Foo {
/* this works */
fn get_bar_ref(&self) -> &usize {
match *self {
Bar(ref n) => &n
}
}
/* this doesn't */
fn get_bar_ref_mut(&mut self) -> &mut usize {
match *self {
Bar(ref mut n) => &mut n
}
}
}
But I can't obtain the mutable reference because:
n does not live long enough
I was able to provide both variants of similar functions accessing other contents of Foo that are Boxed - why does the mutable borrow (and why only it) fail with an unboxed primitive?
You need to replace Bar(ref mut n) => &mut n with Bar(ref mut n) => n.
When you use ref mut n in Bar(ref mut n), it creates a mutable
reference to the data in Bar, so the type of n is &mut usize.
Then you try to return &mut n of &mut &mut u32 type.
This part is most likely incorrect.
Now deref coercion kicks in
and converts &mut n into &mut *n, creating a temporary value *n
of type usize, which doesn't live long enough.
These examples show the sample problem:
fn implicit_reborrow<T>(x: &mut T) -> &mut T {
x
}
fn explicit_reborrow<T>(x: &mut T) -> &mut T {
&mut *x
}
fn implicit_reborrow_bad<T>(x: &mut T) -> &mut T {
&mut x
}
fn explicit_reborrow_bad<T>(x: &mut T) -> &mut T {
&mut **&mut x
}
The explicit_ versions show what the compiler deduces through deref coercions.
The _bad versions both error in the exact same way, while the other two compile.
This is either a bug, or a limitation in how lifetimes are currently implemented in the compiler. The invariance of &mut T over T might have something to do with it, because it results in &mut &'a mut T being invariant over 'a and thus more restrictive during inference than the shared reference (&&'a T) case, even though in this situation the strictness is unnecessary.

What's the most straightforward way to chain comparisons, yielding the first non-equal?

It's quite common to compare data with precedence, for a struct which has multiple members which can be compared, or for a sort_by callback.
// Example of sorting a: Vec<[f64; 2]>, sort first by y, then x,
xy_coords.sort_by(
|co_a, co_b| {
let ord = co_a[1].cmp(&co_b[1]);
if ord != std::cmp::Ordering::Equal {
ord
} else {
co_a[0].cmp(&co_b[0])
}
}
);
Is there a more straightforward way to perform multiple cmp functions, where only the first non-equal result is returned?
perform multiple cmp functions, where only the first non-equal result is returned
That's basically how Ord is defined for tuples. Create a function that converts your type into a tuple and compare those:
fn main() {
let mut xy_coords = vec![[1, 0], [-1, -1], [0, 1]];
fn sort_key(coord: &[i32; 2]) -> (i32, i32) {
(coord[1], coord[0])
}
xy_coords.sort_by(|a, b| {
sort_key(a).cmp(&sort_key(b))
});
}
Since that's common, there's a method just for it:
xy_coords.sort_by_key(sort_key);
It won't help your case, because floating point doesn't implement Ord.
One of many possibilities is to kill the program on NaN:
xy_coords.sort_by(|a, b| {
sort_key(a).partial_cmp(&sort_key(b)).expect("Don't know how to handle NaN")
});
See also
Using max_by_key on a vector of floats
How to do a binary search on a Vec of floats?
There are times when you may not want to create a large tuple to compare values which will be ignored because higher priority values will early-exit the comparison.
Stealing a page from Guava's ComparisonChain, we can make a small builder that allows us to use closures to avoid extra work:
use std::cmp::Ordering;
struct OrdBuilder<T> {
a: T,
b: T,
ordering: Ordering,
}
impl<T> OrdBuilder<T> {
fn new(a: T, b: T) -> OrdBuilder<T> {
OrdBuilder {
a: a,
b: b,
ordering: Ordering::Equal,
}
}
fn compare_with<F, V>(mut self, mut f: F) -> OrdBuilder<T>
where F: for <'a> FnMut(&'a T) -> V,
V: Ord,
{
if self.ordering == Ordering::Equal {
self.ordering = f(&self.a).cmp(&f(&self.b));
}
self
}
fn finish(self) -> Ordering {
self.ordering
}
}
This can be used like
struct Thing {
a: u8,
}
impl Thing {
fn b(&self) -> u8 {
println!("I'm slow!");
42
}
}
fn main() {
let a = Thing { a: 0 };
let b = Thing { a: 1 };
let res = OrdBuilder::new(&a, &b)
.compare_with(|x| x.a)
.compare_with(|x| x.b())
.finish();
println!("{:?}", res);
}

Speedup counter game

I'm trying to solve a Rust algorithm question on hackerrank. My answer times out on some of the larger test cases. There are about 5 people who've completed it, so I believe it is possible and I assume they compile in release mode. Is there any speed-ups I'm missing?
The gist of the game is a counter (inp in main) is conditionally reduced and based on who can't reduce it any more, the winner is chosen.
use std::io;
fn main() {
let n: usize = read_one_line().
trim().parse().unwrap();
for _i in 0..n{
let inp: u64 = read_one_line().
trim().parse().unwrap();
println!("{:?}", find_winner(inp));
}
return;
}
fn find_winner(mut n: u64) -> String{
let mut win = 0;
while n>1{
if n.is_power_of_two(){
n /= 2;
}
else{
n -= n.next_power_of_two()/2;
}
win += 1;
}
let winner =
if win % 2 == 0{
String::from("Richard")
} else{
String::from("Louise")
};
winner
}
fn read_one_line() -> String{
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read");
input
}
Your inner loop can be replaced by a combination of builtin functions:
let win = if n > 0 {
n.count_ones() + n.trailing_zeros() - 1
} else {
0
};
Also, instead of allocating a string every time find_winner is called,
a string slice may be returned:
fn find_winner(n: u64) -> &'static str {
let win = if n > 0 {
n.count_ones() + n.trailing_zeros() - 1
} else {
0
};
if win % 2 == 0 {
"Richard"
} else{
"Louise"
}
}
Avoiding memory allocation can help speeding up the application.
At the moment, the read_one_line function is doing one memory allocation per call, which can be avoided if you supply the String as a &mut parameter:
fn read_one_line(input: &mut String) -> &str {
io::stdin().read_line(input).expect("Failed to read");
input
}
Note how I also alter the return type to return a slice (which borrows input): further uses here do not need to modify the original string.
Another improvement is I/O. Rust is all about explicitness, and it means that io::stdin() is raw I/O: each call to read_line triggers interactions with the kernel.
You can (and should) instead used buffered I/O with std::io::BufReader. Build it once, then pass it as an argument:
fn read_one_line<'a, R>(reader: &mut R, input: &'a mut String) -> &'a str
where R: io::BufRead
{
reader.read_line(input).expect("Failed to read");
input
}
Note:
it's easier to make it generic (R) than to specify the exact type of BufReader :)
annotating the lifetime is mandatory because the return type could borrow either parameter
Putting it altogether:
fn read_one_line<'a, R>(reader: &mut R, input: &'a mut String) -> &'a str
where R: io::BufRead
{
reader.read_line(input).expect("Failed to read");
input
}
fn main() {
let mut reader = io::BufReader::new(io::stdin());
let mut input = String::new();
let n: usize = read_one_line(&mut reader, &mut input).
trim().parse().unwrap();
for _i in 0..n{
let inp: u64 = read_one_line(&mut reader, &mut input).
trim().parse().unwrap();
println!("{:?}", find_winner(inp));
}
return;
}
with the bigger win probably being I/O (might even be sufficient in itself).
Don't forget to also apply #John's advices, this way you'll be allocation-free in your main loop!

Resources