| Bytes | Lang | Time | Link |
|---|---|---|---|
| 003 | Python | 241202T113525Z | 138 Aspe |
| 128 | Rust | 230402T120904Z | 138 Aspe |
| 565 | Python 3 PyPy | 230306T040329Z | gsitcia |
| 020 | Wolfram Language Mathematica | 230223T174948Z | ZaMoC |
| 070 | MiniZinc | 230223T181746Z | matteo_c |
Python, with Z3Py
# !pip install z3-solver
from z3 import *
# Set the value of n
n = Int('n') # Or you can set a specific value, e.g., n = 8
n_value = 8 # Example with n = 8
# Create an array q[1..n] of integer variables ranging from 1 to n
q = [Int('q_%i' % i) for i in range(1, n_value + 1)]
# Initialize the solver
s = Solver()
# Add constraints that each q[i] is between 1 and n
for i in range(n_value):
s.add(q[i] >= 1, q[i] <= n_value)
# Define the noattack predicate constraints
for i in range(1, n_value + 1):
for j in range(i + 1, n_value + 1):
qi = q[i - 1]
qj = q[j - 1]
s.add(qi != qj) # Not in the same row
s.add(qi + i != qj + j) # Not in the same major diagonal
s.add(qi - i != qj - j) # Not in the same minor diagonal
# Define the no3inline predicate constraints
for i in range(1, n_value + 1):
for j in range(i + 1, n_value + 1):
for k in range(j + 1, n_value + 1):
qi = q[i - 1]
qj = q[j - 1]
qk = q[k - 1]
s.add(i * (qk - qj) + j * (qi - qk) + k * (qj - qi) != 0)
# Solve the constraints
if s.check() == sat:
m = s.model()
solution = [m.evaluate(q[i]) for i in range(n_value)]
print("Solution:", solution)
else:
print("No solution found.")
Rust, N=128
Adapted from @gsitcia's answer
use std::collections::HashSet;
// use std::env;
use std::iter;
use std::sync::mpsc;
use std::thread;
use rand::rngs::StdRng;
fn gcd(a: i32, b: i32) -> i32 {
if b == 0 {
a.abs()
} else {
gcd(b, a % b)
}
}
fn line(x0: usize, y0: usize, x1: usize, y1: usize) -> (i32, i32) {
let mut a = (y0 as i32) - (y1 as i32);
let mut b = (x1 as i32) - (x0 as i32);
let gcd_val = gcd(a, b);
a /= gcd_val;
b /= gcd_val;
if a < 0 {
(-a, -b)
} else if a == 0 {
if b < 0 {
(0, -b)
} else {
(0, b)
}
} else {
(a, b)
}
}
fn num_conflicts(l: &[usize], x: usize, y: usize) -> usize {
let mut lines = HashSet::new();
let mut n = 0;
for (x1, &y1) in l.iter().enumerate() {
if x1 == x {
continue;
}
let l1 = line(x, y, x1, y1);
if lines.contains(&l1) {
n += 1;
} else {
lines.insert(l1);
}
}
n
}
fn greedy(n: usize) -> Vec<usize> {
let mut l = Vec::with_capacity(n);
for x in 0..n {
let z: Vec<usize> = (0..n).map(|y| num_conflicts(&l, x, y)).collect();
let v = *z.iter().min().unwrap();
let options: Vec<usize> = z
.iter()
.enumerate()
.filter_map(|(y, &k)| if k == v { Some(y) } else { None })
.collect();
let y = options[rand::random::<usize>() % options.len()];
l.push(y);
}
l
}
fn fix_row(n: usize, l: &mut [usize], x: usize) -> Option<bool> {
let y0 = l[x];
let nc = num_conflicts(l, x, y0);
if nc == 0 {
return Some(true);
}
let z: Vec<usize> = (0..n).map(|y| if y != y0 { num_conflicts(l, x, y) } else { nc }).collect();
let v = *z.iter().min().unwrap();
let options: Vec<usize> = z.iter().enumerate().filter_map(|(y, &k)| if k == v { Some(y) } else { None }).collect();
if options == [y0] {
return Some(false);
}
let y = options[rand::random::<usize>() % options.len()];
l[x] = y;
None
}
fn solve(t: (usize, usize)) -> Option<Vec<usize>> {
let (s, n) = t;
let _rng: StdRng = rand::SeedableRng::seed_from_u64(s as u64);
let mut l = greedy(n);
for _ in 0..3 * n {
let outcomes: HashSet<Option<bool>> = (0..n).map(|x| fix_row(n, &mut l, x)).collect();
let all_true = outcomes.iter().all(|outcome| outcome == &Some(true));
if all_true {
return Some(l);
}
if !outcomes.contains(&None) {
break;
}
}
None
}
fn full(n: usize) -> Vec<usize> {
iter::repeat(n)
.enumerate()
.filter_map(solve)
.next()
.unwrap()
}
fn full_mp(n: usize, p: usize) -> Vec<usize> {
let (tx, rx) = mpsc::channel();
let tasks = iter::repeat(n).take(p).enumerate();
for task in tasks {
let tx = tx.clone();
thread::spawn(move || {
let result = solve(task);
if let Some(solution) = result {
tx.send(solution).unwrap();
}
});
}
rx.recv().unwrap()
}
fn main() {
// let args: Vec<String> = env::args().collect();
// if args.len() < 2 {
// eprintln!("Usage: {} n [-j processes]", args[0]);
// std::process::exit(1);
// }
// let n: usize = args[1].parse().expect("n must be an integer");
// let processes: usize = if args.len() >= 4 && args[2] == "-j" {
// args[3].parse().expect("processes must be an integer")
// } else {
// 1
// };
let n: usize = 128;
let processes: usize =1;
let l = if processes > 1 {
full_mp(n, processes)
} else {
full(n)
};
println!(
"{}",
l.iter()
.enumerate()
.all(|(x, &y)| num_conflicts(&l, x, y) == 0)
);
println!("{:?}", l);
}
Python 3 (PyPy), N = 565
import math
import random
import multiprocessing
import itertools
import argparse
def line(x0, y0, x1, y1):
a = y0 - y1
b = x1 - x0
gcd = math.gcd(a, b)
a //= gcd
b //= gcd
if a < 0:
return -a, -b
elif a == 0:
if b < 0:
return 0, -b
return 0, b
return a, b
def num_conflicts(l, x, y):
lines = {(1,1),(1,-1),(1,0),(0,1)}
n = 0
for x1, y1 in enumerate(l):
if x1 == x: continue
l1 = line(x, y, x1, y1)
if l1 in lines:
n += 1
else:
lines.add(l1)
return n
def greedy(n):
l = []
for x in range(n):
z = [num_conflicts(l, x, y) for y in range(n)]
v = min(z)
y = random.choice([y for y, k in enumerate(z) if k == v])
l.append(y)
return l
def fix_row(n, l, x):
y0 = l[x]
nc = num_conflicts(l, x, y0)
if nc == 0: return 1
z = [num_conflicts(l, x, y) if y != y0 else nc for y in range(n)]
v = min(z)
options = [y for y, k in enumerate(z) if k == v]
if options == [y0]: return -1
y = random.choice(options)
l[x] = y
def solve(t):
s, n = t
random.seed(s)
l = greedy(n)
for i in range(3*n):
outcomes = {fix_row(n, l, x) for x in range(n)}
if outcomes == {1}:
return l
if None not in outcomes:
break
def full(n):
return next(filter(None, map(solve, enumerate(itertools.repeat(n)))))
def full_mp(n, p):
with multiprocessing.Pool(p) as p:
return next(filter(None, p.imap_unordered(solve, enumerate(itertools.repeat(n)))))
def main():
parser = argparse.ArgumentParser(
prog = "queensthree",
description = "Finds solution for the no-three-in-a-line n-queens problem"
)
parser.add_argument('n', help='number of rows/columns', type=int)
parser.add_argument('-j', default=1, help='number of processes to use (defaults to 1)', type=int)
args = parser.parse_args()
if args.j > 1:
l = full_mp(args.n, args.j)
else:
l = full(args.n)
print(all(num_conflicts(l, x, l[x]) == 0 for x in range(args.n)))
print(l)
if __name__ == '__main__':
main()
Uses the Min-conflicts algorithm. The gist of the algorithm is to start with a random arrangement of the queens, and repeat the step of choosing a random queen that is in conflict (in check or in a line with two other queens), and move it to a square in the same row with the least conflicts. After a number of steps, if a solution hasn't been found, start over with a different random arrangement.
Because of how the algorithm works, it occasionally gets lucky and finds a solution on the first try (which is what happened with N=565, the previous solution that could be found in 2 minutes was for N=532).
Wolfram Language (Mathematica), N=20
N=20 in less than a min on my old laptop
queens=20;
put[n_,m_,q_]:=(pl={n,m};s[[n,m]]="X";{i,j}=pl;
While[i>1&&j>1,i--;j--;s[[i,j]]++];
{i,j}=pl;
While[i<q&&j<q,i++;j++;s[[i,j]]++];
{i,j}=pl;
While[i<q&&j>1,i++;j--;s[[i,j]]++];
{i,j}=pl;
While[i>1&&j<q,i--;j++;s[[i,j]]++];
{i,j}=pl;
While[i<q,i++;s[[i,j]]++];
{i,j}=pl;
While[i>1,i--;s[[i,j]]++];
{i,j}=pl;
While[j<q,j++;s[[i,j]]++];
{i,j}=pl;
While[j>1,j--;s[[i,j]]++];)
While[(t=0;
qu=queens;
While[t!=qu,s=Table[0,{i,qu},{j,qu}];
put[RandomInteger[{1,qu}],RandomInteger[{1,qu}],qu];
t=1;
While[!FreeQ[s,0],
pos=Position[s,0];
put[First@#,Last@#,qu]&[RandomChoice@pos];t++];];
posi=Position[s,"X"];
Select[(Last@#/First@#&[First@Differences[#]])&/@#&/@(Partition[#,2,1]&/@Subsets[posi,{3}]),SameQ@@#&])!={}]
s=s/. a_/;IntegerQ@a:>".";
MiniZinc, N = 70
int: n;
array [1..n] of var 1..n: q;
predicate
noattack(int: i, int: j, var int: qi, var int: qj) =
qi != qj /\
qi + i != qj + j /\
qi - i != qj - j;
constraint
forall (i in 1..n, j in i+1..n) (
noattack(i, j, q[i], q[j])
);
predicate
no3inline(int: i, int: j, int: k, var int: qi, var int: qj, var int: qk) =
i * (qk - qj) + j * (qi - qk) + k * (qj - qi) != 0;
constraint
forall (i in 1..n, j in i+1..n, k in j+1..n) (
no3inline(i, j, k, q[i], q[j], q[k])
);
solve :: int_search(q, first_fail, indomain_random)
satisfy;
Adapted from https://github.com/MiniZinc/minizinc-benchmarks/blob/master/queens/queens.mzn.
Solution for \$N = 70\$ in less than 9s on i5-6300U.
It is worth noting that the time required does not always increase when increasing \$N\$. For example, for \$N = 69\$, 2 minutes were not enough.
The goal of this answer is not to show off my skills, but the power of MiniZinc and constraint programming in general.