Skip to content

Commit c5b5c06

Browse files
author
sn-chr
committed
feat: add 2-SAT solver (#345)
* Implement 2-SAT solver * Use `abs_diff` for numbers instead of manually calculating it * Fix formatting
1 parent 6a6b1d0 commit c5b5c06

File tree

5 files changed

+124
-2
lines changed

5 files changed

+124
-2
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
* [Tarjan's Strongly Connected Components](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/strongly_connected_components.rs)
6060
* [Centroid Decomposition](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/centroid_decomposition.rs)
6161
* [Dinic's Max Flow](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/dinic_maxflow.rs)
62+
* [2-SAT Problem](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/two_satisfiability.rs)
6263
* [Lib](https://github.com/TheAlgorithms/Rust/blob/master/src/lib.rs)
6364
* Math
6465
* [Baby-Step Giant-Step Algorithm](https://github.com/TheAlgorithms/Rust/blob/master/src/math/baby_step_giant_step.rs)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ These are for demonstration purposes only.
4545
- [x] [Topological sorting](./src/graph/topological_sort.rs)
4646
- [x] [Centroid Decomposition](./src/graph/centroid_decomposition.rs)
4747
- [x] [Dinic's Max Flow](./src/graph/dinic_maxflow.rs)
48+
- [x] [2-SAT Problem](./src/graph/two_satisfiability.rs)
4849

4950
## [Math](./src/math)
5051
- [x] [Baby-Step Giant-Step Algorithm](./src/math/baby_step_giant_step.rs)

src/graph/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod prim;
1414
mod prufer_code;
1515
mod strongly_connected_components;
1616
mod topological_sort;
17+
mod two_satisfiability;
1718

1819
pub use self::bellman_ford::bellman_ford;
1920
pub use self::breadth_first_search::breadth_first_search;
@@ -31,3 +32,4 @@ pub use self::prim::{prim, prim_with_start};
3132
pub use self::prufer_code::{prufer_decode, prufer_encode};
3233
pub use self::strongly_connected_components::StronglyConnectedComponents;
3334
pub use self::topological_sort::topological_sort;
35+
pub use self::two_satisfiability::solve_two_satisfiability;

src/graph/two_satisfiability.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use super::strongly_connected_components::StronglyConnectedComponents as SCCs;
2+
3+
pub type Condition = (i64, i64);
4+
type Graph = Vec<Vec<usize>>;
5+
6+
#[inline]
7+
fn variable(var: i64) -> usize {
8+
if var < 0 {
9+
(((-var) << 1) + 1) as usize
10+
} else {
11+
(var << 1) as usize
12+
}
13+
}
14+
15+
/// Returns an assignment that satisfies all the constraints, or a variable
16+
/// that makes such an assignment impossible. Variables should be numbered
17+
/// from 1 to n, and a negative number -m corresponds to the negated variable
18+
/// m. For more information about this problem, please visit:
19+
/// https://en.wikipedia.org/wiki/2-satisfiability
20+
pub fn solve_two_satisfiability(
21+
expression: &[Condition],
22+
num_variables: usize,
23+
) -> Result<Vec<bool>, i64> {
24+
let num_verts = (num_variables + 1) << 1;
25+
let mut result = Vec::new();
26+
let mut sccs = SCCs::new(num_verts);
27+
let mut adj = Graph::new();
28+
adj.resize(num_verts, vec![]);
29+
expression.iter().for_each(|cond| {
30+
let v1 = variable(cond.0);
31+
let v2 = variable(cond.1);
32+
adj[v1 ^ 1].push(v2);
33+
adj[v2 ^ 1].push(v1);
34+
});
35+
sccs.find_components(&adj);
36+
result.resize(num_variables + 1, false);
37+
for var in (2..num_verts).step_by(2) {
38+
if sccs.component[var] == sccs.component[var ^ 1] {
39+
return Err((var >> 1) as i64);
40+
}
41+
// if a variable isn't
42+
if sccs.component[var] < sccs.component[var ^ 1] {
43+
result[var >> 1] = true;
44+
}
45+
}
46+
Ok(result)
47+
}
48+
49+
#[cfg(test)]
50+
mod tests {
51+
use std::thread;
52+
53+
use super::*;
54+
55+
fn check_answer(expression: &[Condition], answers: &[bool]) -> bool {
56+
let mut ok = true;
57+
for &(c1, c2) in expression {
58+
let mut cv = false;
59+
if c1 < 0 {
60+
cv |= !answers[-c1 as usize];
61+
} else {
62+
cv |= answers[c1 as usize];
63+
}
64+
if c2 < 0 {
65+
cv |= !answers[-c2 as usize];
66+
} else {
67+
cv |= answers[c2 as usize];
68+
}
69+
ok &= cv;
70+
}
71+
ok
72+
}
73+
#[test]
74+
fn basic_test() {
75+
let conds = vec![(1, 1), (2, 2)];
76+
let res = solve_two_satisfiability(&conds, 2);
77+
assert!(res.is_ok());
78+
assert!(check_answer(&conds, &res.unwrap()));
79+
80+
let conds = vec![(1, 2), (-2, -2)];
81+
let res = solve_two_satisfiability(&conds, 2);
82+
assert!(res.is_ok());
83+
assert!(check_answer(&conds, &res.unwrap()));
84+
85+
let conds = vec![];
86+
let res = solve_two_satisfiability(&conds, 2);
87+
assert!(res.is_ok());
88+
assert!(check_answer(&conds, &res.unwrap()));
89+
90+
let conds = vec![(-1, -1), (-2, -2), (1, 2)];
91+
let res = solve_two_satisfiability(&conds, 2);
92+
assert!(res.is_err());
93+
}
94+
95+
#[test]
96+
#[ignore]
97+
fn big_test() {
98+
// We should spawn a new thread and set its stack size to something
99+
// big (256MB in this case), because doing DFS (for finding SCCs) is
100+
// a stack-intensive operation. 256MB should be enough for 3e5
101+
// variables though.
102+
let builder = thread::Builder::new().stack_size(256 * 1024 * 1024);
103+
let handler = builder
104+
.spawn(|| {
105+
let num_conds = 3e5 as i64;
106+
let mut conds = vec![];
107+
for i in 1..num_conds {
108+
conds.push((i, -(i + 1)));
109+
}
110+
conds.push((num_conds, num_conds));
111+
let res = solve_two_satisfiability(&conds, num_conds as usize);
112+
assert!(res.is_ok());
113+
assert!(check_answer(&conds, &res.unwrap()));
114+
})
115+
.unwrap();
116+
handler.join().unwrap();
117+
}
118+
}

src/math/pollard_rho.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn pollard_rho_customizable(
7373
{
7474
small_iteration += 1;
7575
x = advance(x, c, number);
76-
let diff = (x as i128 - y as i128).abs() as u128;
76+
let diff = x.abs_diff(y);
7777
remainder = (remainder * diff) % number as u128;
7878
}
7979
current_gcd = gcd(remainder as u64, number);
@@ -87,7 +87,7 @@ fn pollard_rho_customizable(
8787
if current_gcd == number {
8888
while current_gcd == 1 {
8989
x_start = advance(x_start, c, number);
90-
current_gcd = gcd((x_start as i128 - y as i128).abs() as u64, number);
90+
current_gcd = gcd(x_start.abs_diff(y) as u64, number);
9191
}
9292
}
9393
current_gcd

0 commit comments

Comments
 (0)