Sudoku generation backtracking creating incorrect boards - algorithm

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.

Related

Finding Max Value Below Zero

I'm trying to get the max value from a diff, n[i] - n[i-1], timeseries. The first value is always zero from the slice, here is the code:
func MaxBelowZero(n ...float64) float64 {
var maxValue float64
if len(n) == 1 {
return n[0]
} else if len(n) == 0 {
return 0.
}
for i := range n {
if i == 0 {
maxValue = math.SmallestNonzeroFloat64
continue
}
if maxValue < n[i] && n[i] < 0 {
maxValue = n[i]
}
}
return maxValue
}
var sliceTest = []float64{0, 1, 2, -1, -2, -10, 10, 20}
MaxBelowZero(sliceTest...)
Output: 5e-324
It supossed to be -1. What am I doing wrong? I would appreciate some help.
The code in playground: link
math.SmallestNonzeroFloat64 is the number closest to 0 that is not 0, not the number furthest from zero. Try this:
go playground
func MaxBelowZero(values ...float64) float64 {
if len(values) == 0 {
return 0
} else if len(values) == 1 {
return values[0]
}
max := -math.MaxFloat64
for _, n := range values {
if n >= 0 {
continue
}
if n > max {
max = n
}
}
return max
}
func MaxBelowZero(n ...float64) float64 {
maxValue := -math.MaxFloat64 // Initial value must be negative
if len(n) == 1 {
return n[0]
} else if len(n) == 0 {
return 0.
}
for i := 1; i < len(n); i++ {
diff := n[i] - n[i-1] // Correct logic here
if diff > maxValue && diff < 0 {
maxValue = diff
}
}
return maxValue
}
You could reverse sort the slice, then find first negative number:
package main
import "sort"
func main() {
a := []float64{0, 1, 2, -1, -2, -10, 10, 20}
sort.Slice(a, func(d, e int) bool {
return a[e] < a[d]
})
n := sort.Search(len(a), func(n int) bool {
return a[n] < 0
})
println(a[n] == -1)
}
Or sort by sign, then by absolute value:
package main
import "math"
type sFunc func(a, b float64) bool
var sFuncs = []sFunc{
func(a, b float64) bool {
return math.Copysign(1, a) < math.Copysign(1, b)
},
func(a, b float64) bool {
return math.Abs(a) < math.Abs(b)
},
}
Result:
package main
import (
"fmt"
"sort"
)
func main() {
floats := []float64{0, 1, 2, -1, -2, -10, 10, 20}
sort.Slice(floats, func(a, b int) bool {
fa, fb := floats[a], floats[b]
for _, sf := range sFuncs {
if sf(fa, fb) {
return true
}
if sf(fb, fa) {
break
}
}
return false
})
fmt.Println(floats) // [-1 -2 -10 0 1 2 10 20]
}
https://golang.org/pkg/sort#Search
https://golang.org/pkg/sort#Slice

Is my golang func run time analysis correct?

enter image description here
This code snippet is for give two slice of binary number a1 and a2 to return sum slice r1, and I want to figure out how long spend with this code snippet figure out the result.
and I figure out the factorial result.
Is my analysis right?
my analysis for time complexity is:
cn + (n*n!) + c
the Code is:
func BinaryPlus(a1 []int, a2 []int) []int {
var r1 = make([]int, len(a1), 2*(len(a1)))
for i := 0; i < len(a1); i++ {
r1[i] = a1[i] + a2[i]
}
// 二分反转
ReverseSlice(r1)
r1 = append(r1, 0)
final := 0
for i := 0; final != 1; i++ {
isOver := 1
for j := 0; j < len(r1); j++ {
if r1[j] > 1 {
r1[j] = r1[j] % 2
r1[j+1] += 1
if r1[j+1] > 1 {
isOver = 0
}
}
}
if isOver == 1 {
final = 1
}
}
// 二分反转
ReverseSlice(r1)
return r1
}
func ReverseSlice(s interface{}) {
n := reflect.ValueOf(s).Len()
swap := reflect.Swapper(s)
for i, j := 0, n-1; i < j; i, j = i+1, j-1 {
swap(i, j)
}
}
It is not entirely clear that your code, as written, is correct. The size cap for your result array could be too small. Consider the case that len(a2) > 2*len(a1): the r1 := make(...) will not reserve enough in this case. Further, the initial for loop will miss adding in the more significant bits of a2.
Binary addition should have no more than O(n) complexity. You can do it with a single for loop. n = 1+max(len(a1),len(a2)):
package main
import (
"fmt"
"reflect"
)
func BinaryPlus(a1 []int, a2 []int) []int {
reserve := len(a1) + 1
if x := len(a2) + 1; x > reserve {
reserve = x
}
hold := 0
maxBit := 1
ans := make([]int, reserve)
for i := 1; i <= reserve; i++ {
hold = hold / 2
if i <= len(a1) {
hold += a1[len(a1)-i]
}
if i <= len(a2) {
hold += a2[len(a2)-i]
}
ans[reserve-i] = hold & 1
if hold != 0 && i > maxBit {
maxBit = i
}
}
return ans[reserve-maxBit:]
}
func main() {
tests := []struct {
a, b, want []int
}{
{
a: []int{1},
b: []int{0},
want: []int{1},
},
{
a: []int{1, 0},
b: []int{0, 0, 1},
want: []int{1, 1},
},
{
a: []int{1, 0, 0, 1},
b: []int{1, 1, 1, 1, 0},
want: []int{1, 0, 0, 1, 1, 1},
},
{
a: []int{0, 0},
b: []int{0, 0, 0, 0, 0},
want: []int{0},
},
}
bad := false
for i := 0; i < len(tests); i++ {
t := tests[i]
c := BinaryPlus(t.a, t.b)
if !reflect.DeepEqual(c, t.want) {
fmt.Println(t.a, "+", t.b, "=", c, "; wanted:", t.want)
bad = true
}
}
if bad {
fmt.Println("FAILED")
} else {
fmt.Println("PASSED")
}
}

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)

Inconsistent append behavior in Go?

I'm writing a function that returns the vertical order traversal of a binary tree's node values. (ie, from top to bottom, column by column). Here's an example of expected input and output:
Input: [3,9,8,4,0,1,7,null,null,null,2,5] (0's right child is 2 and 1's left child is 5)
3
/\
/ \
9 8
/\ /\
/ \/ \
4 01 7
/\
/ \
5 2
Output:
[
[4],
[9,5],
[3,0,1],
[8,2],
[7]
]
My function outputs everything as expected except for [8,2]—I'm getting [2,8] instead:
[[4] [9 5] [3 0 1] [2 8] [7]]
This is what my function looks like:
func verticalOrder(root *TreeNode) [][]int {
if root == nil {
return [][]int{}
}
var (
hd int
m map[int][]int
vals [][]int
min int
max int
traverse func(t *TreeNode, hd int, m map[int][]int)
)
m = make(map[int][]int)
min = 0
max = 0
traverse = func(t *TreeNode, hd int, m map[int][]int) {
if t == nil {
return
}
m[hd] = append(m[hd], t.Val)
if max < hd {
max = hd
}
if min > hd {
min = hd
}
traverse(t.Left, hd-1, m)
traverse(t.Right, hd+1, m)
}
traverse(root, hd, m)
for i := min; i <= max; i++ {
vals = append(vals, m[i])
}
return vals
}
Essentially, I'm using a hash map to keep track of nodes with the same horizontal distance, and appending them to an array. What I'm trying to figure out is how come my output works properly with 9 and 5 but not with 8 and 2? Any feedback is much appreciated!
Steps to reproduce
Run the code below. You'll get an output of [[4] [9 5] [3 0 1] [2 8] [7]]; the output should be [[4] [9 5] [3 0 1] [8 2] [7]]. The thing that's tripping me up is [9 5] is appending in the correct, vertical order, whereas [2 8] is not despite being similar, so I'm not too sure where to go from here.
package main
import "fmt"
// TreeNode is a binary tree node.
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func verticalOrder(root *TreeNode) [][]int {
if root == nil {
return [][]int{}
}
var (
hd int
m map[int][]int
vals [][]int
min int
max int
traverse func(t *TreeNode, hd int, m map[int][]int)
)
m = make(map[int][]int)
min = 0
max = 0
traverse = func(t *TreeNode, hd int, m map[int][]int) {
if t == nil {
return
}
m[hd] = append(m[hd], t.Val)
if max < hd {
max = hd
}
if min > hd {
min = hd
}
traverse(t.Left, hd-1, m)
traverse(t.Right, hd+1, m)
}
traverse(root, hd, m)
for i := min; i <= max; i++ {
vals = append(vals, m[i])
}
return vals
}
func main() {
root := &TreeNode{
Val: 3,
Left: &TreeNode{
Val: 9,
Left: &TreeNode{
Val: 4,
Left: nil,
Right: nil,
},
Right: &TreeNode{
Val: 0,
Left: nil,
Right: &TreeNode{
Val: 2,
Left: nil,
Right: nil,
},
},
},
Right: &TreeNode{
Val: 8,
Left: &TreeNode{
Val: 1,
Left: &TreeNode{
Val: 5,
Left: nil,
Right: nil,
},
Right: nil,
},
Right: &TreeNode{
Val: 7,
Left: nil,
Right: nil,
},
},
}
fmt.Println(verticalOrder(root))
}
I has nothing to do with append.
You are doing a DFS-traversal of a binary tree, the order is called DFS-order of that tree. Thing is that the DFS-order of the tree is not from top to bottom.
Your code visit node 2 before node 8, so the code m[hd] = append(m[hd], t.Val) makes [2 8] instead of [8 2]. There is nothing inconsistent.
To fix the problem, you can either use a BFS to traverse the tree, or, you can keep the depth info into m and sort each m[hd] accordingly.
Generally speaking, BFS is a better idea, but a sort is quickly hackable.

How do I partially sort a Vec or slice?

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

Resources