Задача Кода - Склеивание Тетраэдров Вместе

  • Автор темы Dchornej
  • Обновлено
  • 22, Oct 2024
  • #1

(Эта задача существует для расширения последовательности А276272 в Электронной энциклопедии целочисленных последовательностей и, возможно, создать новую последовательность OEIS.1.)

Это программа, в которой вам придется написать код для вычисления как можно большего количества членов этой последовательности.


Фон

Задача кода - Склеивание тетраэдров вместе Задача кода - Склеивание тетраэдров вместе Задача кода - Склеивание тетраэдров вместе Задача кода - Склеивание тетраэдров вместе

А политет это своего рода полиформа который создается путем склеивания правильных тетраэдров лицом к лицу таким образом, чтобы ни одна из внутренних частей тетраэдров не перекрывалась. Они считаются до вращения в трехмерном пространстве, но нет отражение, поэтому, если конкретная форма является хиральной, она учитывается дважды: один раз для самой себя и один раз для ее зеркального отображения.

  • \$A267272(1) = 1\$, потому что из одной такой конфигурации можно составить один тетраэдр.
  • \$A267272(2) = 1\$, так как из-за симметрии тетраэдров приклеивание к грани такое же, как и к любой другой грани. Это дает вам треугольная бипирамида.
  • \$A267272(3) = 1\$, поскольку сама треугольная бипирамида симметрична, поэтому добавление тетраэдра к любой грани приводит к двунаправленный тетраэдр.
  • \$A267272(4) = 4\$, поскольку существует три различных способа добавления тетраэдра к смещенному тетраэдру, и один из них является киральным (поэтому он учитывается дважды). Они показаны на анимированных GIF-файлах выше.
  • \$A267272(5) = 10\$, как показано в каталоге Джорджа Зичермана. Три из них являются хиральными и четыре — ахиральными.

Правила

Запускайте свой код столько, сколько захотите. Победителем этого конкурса станет пользователь, опубликовавший наибольшее количество членов последовательности вместе со своим кодом. Если два пользователя публикуют одинаковое количество терминов, то побеждает тот, кто опубликует свой последний термин раньше.

(Как только будут вычислены дополнительные термины, тот, кто вычислил код, может добавить новые термины в OEIS, или я могу добавить их и перечислить участников, если он или она пожелает.)


1 Последовательность, которой (предположительно) еще нет в OEIS, представляет собой количество политетов до отражения (т.е. хиральные соединения учитываются один раз, а не дважды). Первые несколько значений этой альтернативной последовательности можно найти по ссылке Джорджа Зичермана.

#code-challenge #code-challenge #geometry #combinatorics #polyomino

Dchornej


Рег
14 Nov, 2010

Тем
73

Постов
199

Баллов
594
  • 26, Oct 2024
  • #2

Ржавчина, 11 (надеюсь, правильных) терминов

 
 -v 

Попробуйте онлайн! Нижний колонтитул содержит некоторые реализации совместимости, поскольку Rust от TIO немного устарел. (это уже было сообщил и добавил в список.)

Выход:

1: 1 [0ms] 2: 1 [0ms] 3: 1 [2ms] 4: 4 [8ms] 5: 10 [35ms] 6: 39 [139ms] 7: 164 [738ms] 8: 767 [4328ms] 9: 3656 [31298ms] 10: 18186 [287871ms] 11: 91532 [3154716ms]

Как видите, он также сообщает, сколько времени это заняло. (Это суммарное время.) Вы можете использовать use ::std::ops::{Add, Sub, Mul}; use ::std::rc::Rc; use ::std::hash::{Hash, Hasher}; use ::std::collections::HashSet; use ::std::iter; use ::std::fmt::{self, Formatter, Display}; use ::std::time::Instant; type Coord = i128; #[derive(Copy, Clone, PartialEq, Eq, Debug)] struct Vec3([Coord; 3]); impl Vec3 { fn dot(self, other: Vec3) -> Coord { self.0.iter().zip(other.0.iter()).map(|(a, b)| a * b).sum() } } impl Display for Vec3 { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "({}, {}, {})", self.0[0], self.0[1], self.0[2]) } } impl Mul<Vec3> for Coord { type Output = Vec3; fn mul(self, mut vec: Vec3) -> Vec3 { for i in &mut vec.0 { *i *= self; } vec } } impl Add for Vec3 { type Output = Vec3; fn add(mut self, other: Vec3) -> Vec3 { for (i, n) in self.0.iter_mut().enumerate() { *n += other.0[i]; } self } } impl Sub for Vec3 { type Output = Vec3; fn sub(self, other: Vec3) -> Vec3 { self + (-1 * other) } } #[derive(Clone, Debug)] struct Tetrahedron([Vec3; 4]); impl Default for Tetrahedron { fn default() -> Tetrahedron { Tetrahedron([ Vec3([-1, -1, -1]), Vec3([-1, 1, 1]), Vec3([ 1, -1, 1]), Vec3([ 1, 1, -1]), ]) } } impl Display for Tetrahedron { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "Tetrahedron({}, {}, {}, {})", self.0[0], self.0[1], self.0[2], self.0[3]) } } impl Tetrahedron { fn collides(&self, other: &Tetrahedron) -> bool { let mut othervecs = [3 * self.0[1], 3 * self.0[2], 3 * self.0[3]]; let sum = self.0[0] + self.0[1] + self.0[2] + self.0[3]; let mut same = 0; for (i, &vec) in self.0.iter().enumerate() { let sum = sum - vec; let vec = 3 * vec; let through = vec - sum; for (j, &u) in other.0.iter().enumerate() { let u = 3 * u; if u == vec { same += 1 } let up = (sum - u).dot(through); for &v in &other.0[j+1..] { let v = 3 * v; let edge = v - u; let ep = edge.dot(through); if up.signum() != ep.signum() || up.abs() >= ep.abs() { continue } let intersection = ep * u + up * edge; if othervecs.iter().enumerate().all(|(i, &ov)| { let mid = othervecs[(i+1)%3] + othervecs[(i+2)%3]; let ov = 2 * ov - mid; (2 * intersection - ep * mid).dot(ep * ov) > 0 }) { return true } } } if i != 3 { othervecs[i] = vec; } } if same == 4 { panic!("EQUAL TETRAHEDRA IN .collides()"); } false } // Mirroring also scales by 3 fn mirror(&self, i: usize) -> Tetrahedron { let mut sum = Vec3([0; 3]); for (j, &vec) in self.0.iter().enumerate() { if j != i { sum = sum + vec; } } let mut copy = self.clone(); copy.scale(); copy.0[i] = 2 * sum - copy.0[i]; copy.swap(i, 3); copy.rotate_left(i); copy } fn scale(&mut self) { for vec in self.0.iter_mut() { *vec = 3 * *vec; } } fn rotate_left(&mut self, n: usize) { self.0[..3].rotate_left(n) } fn swap(&mut self, a: usize, b: usize) { self.0.swap(a, b) } fn reverse(&mut self) { self.0.reverse() } } type EndpointIter<'a> = Box<dyn Iterator<Item = Endpoint> + 'a>; #[derive(Debug, Clone)] struct Endpoint { tree: Rc<TetraTree>, hedron: Rc<Tetrahedron>, } impl Endpoint { fn iter_endpoints(&self) -> EndpointIter { let mut hedron = Rc::clone(&self.hedron); //Rc::make_mut(&mut hedron).swap(0, 3); Rc::make_mut(&mut hedron).reverse(); Box::new(self.clone().into_iter_directions() .chain(self.tree.iter_endpoints( Rc::new(TetraTree { subtrees: [None, None, None], hedron, }) )) ) } fn into_iter_directions(self) -> EndpointIter<'static> { Box::new(iter::successors(Some(self), |this| { let mut this = this.clone(); Rc::make_mut(&mut this.tree).rotate_left(1); Rc::make_mut(&mut this.hedron).0[1..].rotate_left(1); Some(this) }).take(3)) } fn iter_extensions(&self) -> EndpointIter { let mut tree = self.tree.as_ref().clone(); tree.scale(); let mut hedron = self.hedron.as_ref().clone(); //hedron.swap(0, 3); hedron.reverse(); hedron.scale(); Box::new(Endpoint { tree: Rc::new(tree), hedron: Rc::clone(&self.hedron), }.into_iter_directions().filter_map(|endpoint| { let mut new = endpoint.hedron.mirror(3); new.swap(0, 3); if endpoint.tree.collides(&new) { None } else { let mut hedron = endpoint.hedron; Rc::make_mut(&mut hedron).scale(); Some(Endpoint { tree: Rc::new(TetraTree { subtrees: [Some(endpoint.tree), None, None], hedron, }), hedron: Rc::new(new), }) } }).chain(self.tree.iter_extensions(Rc::new(TetraTree { subtrees: [None, None, None], hedron: Rc::new(hedron), })))) } fn iter_tetrahedra<'a>(&'a self) -> Box<dyn Iterator<Item = &Tetrahedron> + 'a> { Box::new( iter::once(self.hedron.as_ref()).chain(self.tree.iter_tetrahedra()) ) } } impl Default for Endpoint { fn default() -> Endpoint { Endpoint::from(Tetrahedron::default()) } } impl From<Tetrahedron> for Endpoint { fn from(mut hedron: Tetrahedron) -> Endpoint { let mirrored = hedron.mirror(0); hedron.scale(); Endpoint { tree: Rc::new(TetraTree { subtrees: [None, None, None], hedron: Rc::new(mirrored), }), hedron: Rc::new(hedron), } } } impl Hash for Endpoint { fn hash<H: Hasher>(&self, hasher: &mut H) { let mut stuff: Vec<_> = self.tree.hash_helper(1).collect(); stuff.push(self.tree.len()); stuff.sort(); stuff.hash(hasher); } } impl PartialEq for Endpoint { fn eq(&self, other: &Endpoint) -> bool { self.iter_endpoints().any(|ep| ep.tree == other.tree) } } impl Eq for Endpoint {} #[derive(Debug, Clone)] struct TetraTree { subtrees: [Option<Rc<TetraTree>>; 3], hedron: Rc<Tetrahedron>, } impl TetraTree { fn iter_endpoints<'x>(&'x self, behind: Rc<TetraTree>) -> EndpointIter<'x> { let mut iterator = self.subtrees.iter().enumerate() .filter_map(|(i, opt)| opt.as_ref().map(|some| (i, some))); if let Some(first) = iterator.next() { let closure = move |(i, subtree): (usize, &'x Rc<TetraTree>)| -> EndpointIter<'x> { let mut behind = behind.as_ref().clone(); behind.rotate_left(3-i); let mut this = self.clone(); this.rotate_left(i); Rc::make_mut(&mut this.hedron).reverse(); this.subtrees.swap(1, 2); this.subtrees[0] = Some(Rc::new(behind)); for (j, subtree) in this.subtrees.iter_mut().enumerate().skip(1) .filter_map(|(j, s)| s.as_mut().map(|s| (j, s))) { Rc::make_mut(subtree).rotate_left(j); } subtree.iter_endpoints(Rc::new(this)) }; Box::new(closure(first).chain(iterator.flat_map(closure))) } else { let mut hedron = self.hedron.as_ref().clone(); //hedron.swap(0, 3); hedron.reverse(); Endpoint { tree: Rc::clone(&behind), hedron: Rc::new(hedron), }.into_iter_directions() } } fn rotate_left(&mut self, i: usize) { self.subtrees.rotate_left(i); Rc::make_mut(&mut self.hedron).rotate_left(i); } fn collides(&self, hedron: &Tetrahedron) -> bool { self.hedron.collides(hedron) || self.subtrees.iter() .filter_map(Option::as_ref) .any(|subtree| subtree.collides(hedron)) } fn scale(&mut self) { for subtree in self.subtrees.iter_mut().filter_map(Option::as_mut) { Rc::make_mut(subtree).scale(); } Rc::make_mut(&mut self.hedron).scale(); } fn iter_extensions(&self, behind: Rc<TetraTree>) -> EndpointIter { Box::new(self.subtrees.iter().enumerate().flat_map(move |(i, next)| { let mut behind = behind.as_ref().clone(); behind.rotate_left(3-i); let mut this = self.clone(); this.rotate_left(i); let hedron = Rc::make_mut(&mut this.hedron); hedron.reverse(); this.subtrees.swap(1, 2); this.subtrees[0] = Some(Rc::new(behind)); for (j, subtree) in this.subtrees.iter_mut().enumerate().skip(1) .filter_map(|(j, s)| s.as_mut().map(|s| (j, s))) { let subtree = Rc::make_mut(subtree); subtree.scale(); subtree.rotate_left(j); } if let Some(next) = next { hedron.scale(); next.iter_extensions(Rc::new(this)) } else { let mut mirrored = hedron.mirror(3); mirrored.swap(0, 3); hedron.scale(); if this.collides(&mirrored) { Box::new(iter::empty()) as EndpointIter } else { Box::new(iter::once(Endpoint { tree: Rc::new(this), hedron: Rc::new(mirrored), })) } } })) } fn iter_tetrahedra<'a>(&'a self) -> Box<dyn Iterator<Item = &Tetrahedron> + 'a> { Box::new(iter::once(self.hedron.as_ref()).chain( self.subtrees.iter() .filter_map(Option::as_deref) .flat_map(TetraTree::iter_tetrahedra) )) } fn len(&self) -> usize { 1usize + self.subtrees.iter() .filter_map(Option::as_deref).map(TetraTree::len).sum::<usize>() } fn hash_helper<'a>(&'a self, behind: usize) -> Box<dyn Iterator<Item = usize> + 'a> { let sub: Vec<_> = self.subtrees.iter().filter_map(Option::as_deref) .map(|a| (a, a.len())).collect(); let sum = sub.iter() .map(|&(_, len)| len).sum::<usize>() + behind + 1; Box::new(iter::once(behind).chain( sub.into_iter().flat_map(move |(sub, len)| iter::once(len).chain(sub.hash_helper(sum - len))) )) } } impl PartialEq for TetraTree { fn eq(&self, rhs: &TetraTree) -> bool { self.subtrees == rhs.subtrees } } impl Eq for TetraTree {} fn main() { let verbose = std::env::args().skip(1).any(|arg| arg == "-v"); let begin = Instant::now(); println!("1: 1 [{}ms]", begin.elapsed().as_millis()); if verbose { println!("{}\n--", Tetrahedron::default()); } let mut polytets = HashSet::new(); polytets.insert(Endpoint::default()); for i in 2.. { println!("{}: {} [{}ms]", i, polytets.len(), begin.elapsed().as_millis()); if verbose { for polytet in &polytets { for hedron in polytet.iter_tetrahedra() { println!("{}", hedron); } println!("--"); } } polytets = polytets.iter() .flat_map(Endpoint::iter_extensions) .collect(); } } option to list all tetrahedra for each polytet.

У меня нет хорошего способа проверить результаты, кроме A267272(5). Я надеюсь, что это сработает, но я не уверен.

Идея состоит в том, что мы храним политет как дерево тетраэдров, которое также кодирует ориентацию. Но для обнаружения столкновений нам нужны настоящие тетраэдры. Начнем с тетраэдра с вершинами (-1, -1, -1), (-1, 1, 1), (1, -1, 1), (1, 1, -1) и масштабируем все тетраэдры на 3. за каждый срок. Это позволяет избежать необходимости дробей.

Наверное, можно сделать быстрее, но я не знаю как.

 

Dikaja2015


Рег
10 Aug, 2014

Тем
98

Постов
197

Баллов
727
Тем
403,760
Комментарии
400,028
Опыт
2,418,908

Интересно