Решение на (Floating) Points and Vectors от Томислав Николов

Обратно към всички решения

Към профила на Томислав Николов

Резултати

  • 16 точки от тестове
  • 0 бонус точки
  • 16 точки общо
  • 12 успешни тест(а)
  • 3 неуспешни тест(а)

Код

use std::ops::{ Add, Sub, Mul, BitXor, Div};
#[derive(Debug, Clone, Copy)]
pub struct Point {
px: f64,
py: f64,
pz: f64,
}
impl PartialEq<Point> for Point {
fn eq(&self, other: &Point) -> bool {
let eps = std::f64::EPSILON;
if (self.px-other.px).abs() < eps &&
(self.py-other.py).abs() < eps &&
(self.pz-other.pz).abs() < eps {
true
}
else {
false
}
}
fn ne(&self, other: &Point) -> bool {
!(self == other)
}
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Self {
let p = Point {
px: x,
py: y,
pz: z,
};
p
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vector {
vx: f64,
vy: f64,
vz: f64,
}
impl PartialEq<Vector> for Vector {
fn eq(&self, other: &Vector) -> bool {
let eps = std::f64::EPSILON;
if (self.vx-other.vx).abs() < eps &&
(self.vy-other.vy).abs() < eps &&
(self.vz-other.vz).abs() < eps {
true
}
else {
false
}
}
fn ne(&self, other: &Vector) -> bool {
!(self == other)
}
}
impl Add for Vector {
type Output = Vector;
fn add(self, rhs: Vector) -> Self::Output {
Vector::new(self.vx+rhs.vx, self.vy+rhs.vy, self.vz+rhs.vz)
}
}
impl Add<Vector> for Point {
type Output = Point;
fn add(self, rhs: Vector) -> Self::Output {
Point::new(self.px+rhs.vx, self.py+rhs.vy, self.pz+rhs.vz)
}
}
impl Sub for Vector {
type Output = Vector;
fn sub(self, rhs: Vector) -> Self::Output {
Vector::new(self.vx-rhs.vx, self.vy-rhs.vy, self.vz-rhs.vz)
}
}
impl Sub for Point {
type Output = Vector;
fn sub(self, rhs: Point) -> Self::Output {
Vector::new(self.px-rhs.px, self.py-rhs.py, self.pz-rhs.pz)
}
}
impl Mul for Vector {
type Output = f64;
fn mul(self, rhs: Vector) -> f64 {
self.vx*rhs.vx+self.vy*rhs.vy+self.vz*rhs.vz
}
}
impl Mul<Vector> for f64 {
type Output = Vector;
fn mul(self, rhs: Vector) -> Self::Output {
Vector::new(self*rhs.vx, self*rhs.vy, self*rhs.vz)
}
}
impl Div for Vector {
type Output = f64;
fn div(self, rhs: Vector) -> f64 {
self.vx/rhs.vx+self.vy/rhs.vy+self.vz/rhs.vz
}
}
impl BitXor for Vector {
type Output = Vector;
fn bitxor(self, rhs: Vector) -> Self::Output {
Vector::new(self.vy*rhs.vz-self.vz*rhs.vy,
self.vz*rhs.vx-self.vx*rhs.vz,
self.vx*rhs.vy-self.vy*rhs.vx)
}
}
impl Vector {
pub fn new(x: f64, y: f64, z: f64) -> Self {
let v = Vector {
vx: x,
vy: y,
vz: z,
};
v
}
}
#[derive(Debug)]
pub struct Line {
lx: f64,
ly: f64,
lz: f64,
po: Point,
ve: Vector,
}

Не става въпрос за двумерни линии, а за тримерни. Затова се конструират с тримерни точки и вектори. Да вземеш само x и y стойностите fail-ва повечето line тестове.

impl PartialEq<Line> for Line {
fn eq(&self, other: &Line) -> bool
{
let k1 = (other.lx/self.lx).abs();
let k2 = (other.ly/self.ly).abs();
let k3 = (other.lz/self.lz).abs();
let eps = std::f64::EPSILON;
if (k1-k2).abs() < eps &&
(k2-k3).abs() < eps &&
(k3-k1).abs() < eps {
true
}
else {
false
}
}

Логиката тук ми е странно да я проследя. Единственото, което използваш тук за сравнение, е lx-y-z, което са координатите на точка на линията. Изваждаш ratio-тата на тази точка? Не знам каква формула си се опитал да имплементираш, но няма как да можеш да определиш еднаквост на две тримерни линии само от координатите на една точка, лежаща на всяка.

Освен това, можеше вместо три float-а, да запазиш втората точка?

fn ne(&self, other: &Line) -> bool {
!(self == other)
}
}
impl Line {
pub fn from_pp(p1: Point, p2: Point) -> Option<Self> {
if p1 == p2 {
None
} else {
let v0 = p2 - p1;
let l = Line::from_pv(p1, v0);
l
}
}
pub fn from_pv(p: Point, v: Vector) -> Option<Self> {
if v == Vector::new(0.0, 0.0, 0.0) {
None
} else {
let l = Line {
lx: p.px+v.vx,
ly: p.py+v.vy,
lz: p.pz+v.vz,
po: p,
ve: v,
};
Some(l)
}
}
pub fn distance(&self, target: Point) -> f64 {
let to_target = target-self.po;
((to_target^self.ve)/self.ve)
}

Деленето тук е грешно. Във формулата в wikipedia се делят дължините на векторите (vector norm). Реално, делението, което ти си имплементирал върху вектори, не съществува като операция -- то е "скаларно произведение с реципрочния вектор", но не върши работа за много неща. Ако имаше метод len, който да връща дължината на един вектор (корен квадратен от сумата на квадратите на координатите му), щеше да сработи този метод с ((to_target ^ self.ve).len()/self.ve.len())

Може да пробваш да си начертаеш една-две прави с прости координати и да пробваш какво ще ти върне този код за тях. Скоро ще публикувам и пълния тест на същото място, на което е базовия, така че refresh-вай репо-то.

}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20190123-22631-1vbp52a/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 5.45s
     Running target/debug/deps/solution-2e785d603b538f71

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/solution_test-29808948fb50ed3a

running 15 tests
test solution_test::test_equailty_symmetry ... ok
test solution_test::test_equality_basic ... ok
test solution_test::test_equality_floating ... ok
test solution_test::test_line_constructors ... FAILED
test solution_test::test_line_equality_by_points ... ok
test solution_test::test_line_equality_by_points_and_vectors ... ok
test solution_test::test_line_equality_by_vectors ... FAILED
test solution_test::test_line_validity ... ok
test solution_test::test_number_by_vector ... ok
test solution_test::test_number_vector_multiplication_with_precision ... ok
test solution_test::test_point_distance ... FAILED
test solution_test::test_points_minus_points ... ok
test solution_test::test_points_plus_vectors ... ok
test solution_test::test_vector_by_vector ... ok
test solution_test::test_vector_by_vector_cross ... ok

failures:

---- solution_test::test_line_constructors stdout ----
thread 'solution_test::test_line_constructors' panicked at 'assertion failed: `(left != right)`
  left: `Some(Line { lx: 1.0, ly: 1.0, lz: 1.0, po: Point { px: 0.0, py: 0.0, pz: 0.0 }, ve: Vector { vx: 1.0, vy: 1.0, vz: 1.0 } })`,
 right: `Some(Line { lx: 1.0, ly: -1.0, lz: 1.0, po: Point { px: 0.0, py: 0.0, pz: 0.0 }, ve: Vector { vx: 1.0, vy: -1.0, vz: 1.0 } })`', tests/solution_test.rs:165:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

---- solution_test::test_line_equality_by_vectors stdout ----
thread 'solution_test::test_line_equality_by_vectors' panicked at 'assertion failed: `(left == right)`
  left: `Some(Line { lx: 0.1, ly: 0.2, lz: 0.5, po: Point { px: 0.0, py: 0.4, pz: 0.0 }, ve: Vector { vx: 0.1, vy: -0.2, vz: 0.5 } })`,
 right: `Some(Line { lx: 0.2, ly: 0.0, lz: 1.0, po: Point { px: 0.0, py: 0.4, pz: 0.0 }, ve: Vector { vx: 0.2, vy: -0.4, vz: 1.0 } })`', tests/solution_test.rs:226:5

---- solution_test::test_point_distance stdout ----
thread 'solution_test::test_point_distance' panicked at 'assertion failed: (line.distance(p1) - 2.0f64.sqrt() / 2.0).abs() < EPS * 1000.0', tests/solution_test.rs:183:5


failures:
    solution_test::test_line_constructors
    solution_test::test_line_equality_by_vectors
    solution_test::test_point_distance

test result: FAILED. 12 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (3 версии и 5 коментара)

Томислав качи първо решение на 22.11.2018 18:42 (преди почти 7 години)

Томислав качи решение на 25.11.2018 14:47 (преди почти 7 години)

use std::ops::Add;
use std::ops::Sub;
use std::ops::Mul;
use std::ops::BitXor;
#[derive(Debug, Clone, Copy)]
pub struct Point {
px: f64,
py: f64,
pz: f64,
}
impl PartialEq<Point> for Point {
fn eq(&self, other: &Point) -> bool {
let eps = std::f64::EPSILON;
if (self.px-other.px).abs() < eps &&
(self.py-other.py).abs() < eps &&
(self.pz-other.pz).abs() < eps {
true
}
else {
false
}
}
fn ne(&self, other: &Point) -> bool {
!(self == other)
}
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Self {
let p = Point {
px: x,
py: y,
pz: z,
};
p
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vector {
vx: f64,
vy: f64,
vz: f64,
}
impl PartialEq<Vector> for Vector {
fn eq(&self, other: &Vector) -> bool {
let eps = std::f64::EPSILON;
if (self.vx-other.vx).abs() < eps &&
(self.vy-other.vy).abs() < eps &&
(self.vz-other.vz).abs() < eps {
true
}
else {
false
}
}
fn ne(&self, other: &Vector) -> bool {
!(self == other)
}
}
impl Add for Vector {
type Output = Vector;
fn add(self, rhs: Vector) -> Self::Output {
Vector::new(self.vx+rhs.vx, self.vy+rhs.vy, self.vz+rhs.vz)
}
}
impl Add<Vector> for Point {
type Output = Point;
fn add(self, rhs: Vector) -> Self::Output {
Point::new(self.px+rhs.vx, self.py+rhs.vy, self.pz+rhs.vz)
}
}
impl Sub for Vector {
type Output = Vector;
fn sub(self, rhs: Vector) -> Self::Output {
Vector::new(self.vx-rhs.vx, self.vy-rhs.vy, self.vz-rhs.vz)
}
}
impl Sub for Point {
type Output = Vector;
fn sub(self, rhs: Point) -> Self::Output {
Vector::new(self.px-rhs.px, self.py-rhs.py, self.pz-rhs.pz)
}
}
impl Mul for Vector {
type Output = f64;
fn mul(self, rhs: Vector) -> f64 {
self.vx*rhs.vx+self.vy*rhs.vy+self.vz*rhs.vz
}
}
impl Mul<Vector> for f64 {
type Output = Vector;
fn mul(self, rhs: Vector) -> Self::Output {
Vector::new(self*rhs.vx, self*rhs.vy, self*rhs.vz)
}
}
impl BitXor for Vector {
type Output = Vector;
fn bitxor(self, rhs: Vector) -> Self::Output {
Vector::new(self.vy*rhs.vz-self.vz*rhs.vy,
self.vz*rhs.vx-self.vx*rhs.vz,
self.vx*rhs.vy-self.vy*rhs.vx)
}
}
impl Vector {
pub fn new(x: f64, y: f64, z: f64) -> Self {
let v = Vector {
vx: x,
vy: y,
vz: z,
};
v
}
}
#[derive(Debug)]
pub struct Line {
lx: f64,
ly: f64,
+ lz: f64,
}
impl PartialEq<Line> for Line {
fn eq(&self, other: &Line) -> bool
{
let k1 = (other.lx/self.lx).abs();
let k2 = (other.ly/self.ly).abs();
+ let k3 = (other.lz/self.lz).abs();
let eps = std::f64::EPSILON;
- if (k1-k2).abs() < eps {
+ if (k1-k2).abs() < eps &&
+ (k2-k3).abs() < eps &&
+ (k3-k1).abs() < eps {
true
}
else {
false
}
}
fn ne(&self, other: &Line) -> bool {
!(self == other)
}
}
impl Line {
pub fn from_pp(p1: Point, p2: Point) -> Option<Self> {
if p1 == p2 {
None
} else {
let v0 = p2 - p1;
let l = Line::from_pv(p1, v0);
l
}
}
pub fn from_pv(p: Point, v: Vector) -> Option<Self> {
if v == Vector::new(0.0, 0.0, 0.0) {
None
} else {
let l = Line {
lx: p.px+v.vx,
ly: p.py+v.vy,
+ lz: p.pz+v.vz,
};
Some(l)
}
}
pub fn distance(&self, target: Point) -> f64 {
let a = 1.0/self.lx;
let b = 1.0/self.ly;
- let c = -2.0;
- let d1 = ((a*target.px)+(b*target.py)+c).abs();
- let d2 = (a*a+b*b).sqrt();
+ let c = 1.0/self.lz;
+ let d = -3.0;
+ let d1 = ((a*target.px)+(b*target.py)+(c*target.pz)+d).abs();
+ let d2 = (a*a+b*b+c*c).sqrt();
d1/d2

ax + by + cz + d = 0 не е уравнение на права. Това е уравнение на равнина в тримерното пространство. И за нея, a, b, c, d, са параметрите, които дефинират равнината, а не x, y, z.

Предполагам, че ти липсват някакви познания по геометрия, но единственото, което мога да ти препоръчам, е да прочетеш внимателно условието и да прочетеш обстойно линковете от уикипедия, може би дори да погугълнеш малко допълнително, за да си изясниш формулите.

}
}

Томислав качи решение на 25.11.2018 21:23 (преди почти 7 години)

-use std::ops::Add;
+use std::ops::{ Add, Sub, Mul, BitXor, Div};
-use std::ops::Sub;
-
-use std::ops::Mul;
-
-use std::ops::BitXor;
-
#[derive(Debug, Clone, Copy)]
pub struct Point {
px: f64,
py: f64,
pz: f64,
}
impl PartialEq<Point> for Point {
fn eq(&self, other: &Point) -> bool {
let eps = std::f64::EPSILON;
if (self.px-other.px).abs() < eps &&
(self.py-other.py).abs() < eps &&
(self.pz-other.pz).abs() < eps {
true
}
else {
false
}
}
fn ne(&self, other: &Point) -> bool {
!(self == other)
}
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Self {
let p = Point {
px: x,
py: y,
pz: z,
};
p
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vector {
vx: f64,
vy: f64,
vz: f64,
}
impl PartialEq<Vector> for Vector {
fn eq(&self, other: &Vector) -> bool {
let eps = std::f64::EPSILON;
if (self.vx-other.vx).abs() < eps &&
(self.vy-other.vy).abs() < eps &&
(self.vz-other.vz).abs() < eps {
true
}
else {
false
}
}
fn ne(&self, other: &Vector) -> bool {
!(self == other)
}
}
impl Add for Vector {
type Output = Vector;
fn add(self, rhs: Vector) -> Self::Output {
Vector::new(self.vx+rhs.vx, self.vy+rhs.vy, self.vz+rhs.vz)
}
}
impl Add<Vector> for Point {
type Output = Point;
fn add(self, rhs: Vector) -> Self::Output {
Point::new(self.px+rhs.vx, self.py+rhs.vy, self.pz+rhs.vz)
}
}
impl Sub for Vector {
type Output = Vector;
fn sub(self, rhs: Vector) -> Self::Output {
Vector::new(self.vx-rhs.vx, self.vy-rhs.vy, self.vz-rhs.vz)
}
}
impl Sub for Point {
type Output = Vector;
fn sub(self, rhs: Point) -> Self::Output {
Vector::new(self.px-rhs.px, self.py-rhs.py, self.pz-rhs.pz)
}
}
impl Mul for Vector {
type Output = f64;
fn mul(self, rhs: Vector) -> f64 {
self.vx*rhs.vx+self.vy*rhs.vy+self.vz*rhs.vz
}
}
impl Mul<Vector> for f64 {
type Output = Vector;
fn mul(self, rhs: Vector) -> Self::Output {
Vector::new(self*rhs.vx, self*rhs.vy, self*rhs.vz)
}
}
+impl Div for Vector {
+ type Output = f64;
+
+ fn div(self, rhs: Vector) -> f64 {
+ self.vx/rhs.vx+self.vy/rhs.vy+self.vz/rhs.vz
+ }
+}
+
impl BitXor for Vector {
type Output = Vector;
fn bitxor(self, rhs: Vector) -> Self::Output {
Vector::new(self.vy*rhs.vz-self.vz*rhs.vy,
self.vz*rhs.vx-self.vx*rhs.vz,
self.vx*rhs.vy-self.vy*rhs.vx)
}
}
impl Vector {
pub fn new(x: f64, y: f64, z: f64) -> Self {
let v = Vector {
vx: x,
vy: y,
vz: z,
};
v
}
}
#[derive(Debug)]
pub struct Line {
lx: f64,
ly: f64,
lz: f64,
+ po: Point,
+ ve: Vector,
}
impl PartialEq<Line> for Line {
fn eq(&self, other: &Line) -> bool
{
let k1 = (other.lx/self.lx).abs();
let k2 = (other.ly/self.ly).abs();
let k3 = (other.lz/self.lz).abs();
let eps = std::f64::EPSILON;
if (k1-k2).abs() < eps &&
(k2-k3).abs() < eps &&
(k3-k1).abs() < eps {
true
}
else {
false
}
}

Логиката тук ми е странно да я проследя. Единственото, което използваш тук за сравнение, е lx-y-z, което са координатите на точка на линията. Изваждаш ratio-тата на тази точка? Не знам каква формула си се опитал да имплементираш, но няма как да можеш да определиш еднаквост на две тримерни линии само от координатите на една точка, лежаща на всяка.

Освен това, можеше вместо три float-а, да запазиш втората точка?

fn ne(&self, other: &Line) -> bool {
!(self == other)
}
}
impl Line {
pub fn from_pp(p1: Point, p2: Point) -> Option<Self> {
if p1 == p2 {
None
} else {
let v0 = p2 - p1;
let l = Line::from_pv(p1, v0);
l
}
}
pub fn from_pv(p: Point, v: Vector) -> Option<Self> {
if v == Vector::new(0.0, 0.0, 0.0) {
None
} else {
let l = Line {
lx: p.px+v.vx,
ly: p.py+v.vy,
lz: p.pz+v.vz,
+ po: p,
+ ve: v,
};
Some(l)
}
}
pub fn distance(&self, target: Point) -> f64 {
- let a = 1.0/self.lx;
- let b = 1.0/self.ly;
- let c = 1.0/self.lz;
- let d = -3.0;
- let d1 = ((a*target.px)+(b*target.py)+(c*target.pz)+d).abs();
- let d2 = (a*a+b*b+c*c).sqrt();
- d1/d2
+ let to_target = target-self.po;
+ ((to_target^self.ve)/self.ve)
}

Деленето тук е грешно. Във формулата в wikipedia се делят дължините на векторите (vector norm). Реално, делението, което ти си имплементирал върху вектори, не съществува като операция -- то е "скаларно произведение с реципрочния вектор", но не върши работа за много неща. Ако имаше метод len, който да връща дължината на един вектор (корен квадратен от сумата на квадратите на координатите му), щеше да сработи този метод с ((to_target ^ self.ve).len()/self.ve.len())

Може да пробваш да си начертаеш една-две прави с прости координати и да пробваш какво ще ти върне този код за тях. Скоро ще публикувам и пълния тест на същото място, на което е базовия, така че refresh-вай репо-то.

}