Решение на (Floating) Points and Vectors от Даяна Веселинова

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

Към профила на Даяна Веселинова

Резултати

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

Код

use std::ops::*;

Смислено. Обикновено е добре да use-ваме неща едно по едно за да не получим някой конфликт с имената, но за домашно, в което ще се дефинират оператори, това е удобно и ясно.

#[derive(Debug, Clone, Copy)]
pub struct Point {
x:f64,
y:f64,
z:f64,
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Point{x,y,z}
}
}
impl PartialEq for Point {
fn eq(&self, other: &Point)->bool{
let isx = if self.x < 0.0 && other.x < 0.0 {
(self.x.abs() - other.x.abs()).abs() < std::f64::EPSILON
} else if self.x >= 0.0 && other.x >= 0.0 {
(self.x - other.x).abs() < std::f64::EPSILON
} else {
false
};

Това може да се опрости така:

let isx = (self.x - other.x).abs() < std::f64::EPSILON;

Ако и двете са отрицателни, минуса ще обърне това отдясно и пак ще се "неутрализират". Ако и двете са положителни, същото. Ако едното е положително, а другото е отрицателно, ще получим абсолютната разлика между тях. която пак може да е достатъчно малка да ги считаме за равни.

let isy = if self.y < 0.0 && other.y < 0.0 {
(self.y.abs() - other.y.abs()).abs() < std::f64::EPSILON
} else if self.y >= 0.0 && other.y >= 0.0 {
(self.y - other.y).abs() < std::f64::EPSILON
} else {
false
};
let isz = if self.z < 0.0 && other.z < 0.0 {
(self.z.abs() - other.z.abs()).abs() < std::f64::EPSILON
} else if self.z >= 0.0 && other.z >= 0.0 {
(self.z - other.z).abs() < std::f64::EPSILON
} else {
false
};
return isx && isy && isz;
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vector {
x:f64,
y:f64,
z:f64,
}
impl Vector {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Vector{x,y,z}
}
}
impl PartialEq for Vector{
fn eq(&self, other: &Vector)->bool{
let isx = if self.x < 0.0 && other.x < 0.0 {
(self.x.abs() - other.x.abs()).abs() < std::f64::EPSILON
} else if self.x >= 0.0 && other.x >= 0.0 {
(self.x - other.x).abs() < std::f64::EPSILON
} else {
false
};
let isy = if self.y < 0.0 && other.y < 0.0 {
(self.y.abs() - other.y.abs()).abs() < std::f64::EPSILON
} else if self.y >= 0.0 && other.y >= 0.0 {
(self.y - other.y).abs() < std::f64::EPSILON
} else {
false
};
let isz = if self.z < 0.0 && other.z < 0.0 {
(self.z.abs() - other.z.abs()).abs() < std::f64::EPSILON
} else if self.z >= 0.0 && other.z >= 0.0 {
(self.z - other.z).abs() < std::f64::EPSILON
} else {
false
};
return isx && isy && isz;
}
}
impl Add<Point> for Vector{
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl Add<Vector> for Point{
type Output = Point;
fn add(self, other: Vector) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
z: self.z+other.z,
}
}
}
impl Add<Vector> for Vector{
type Output = Vector;
fn add(self, other: Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl Sub<Point> for Point{
type Output = Vector;
fn sub(self, other: Point) -> Vector {
Vector {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}
impl Mul<Vector> for f64{
type Output = Vector;
fn mul(self, other: Vector) -> Vector {
Vector {
x: self * other.x,
y: self * other.y,
z: self * other.z,
}
}
}
impl Mul<Vector> for Vector{
type Output = f64;
fn mul(self, other: Vector) -> f64 {
return self.x * other.x + self.y * other.y + self.z * other.z;
}
}
impl BitXor<Vector> for Vector{
type Output = Vector;
fn bitxor(self, other: Vector) -> Vector{
Vector{
x: self.y * other.z - self.z * other.y,
y: self.z * other.x - self.x * other.z,
z: self.x * other.y - self.y * other.x,
}
}
}
#[derive(Debug)]
pub struct Line {
p1: Point,
p2: Point,
}
impl Line {
pub fn from_pp(p1: Point, p2: Point) -> Option<Self> {
if p1==p2 {
return None;
}
Some(Line {p1:p1, p2:p2})
}
pub fn from_pv(p: Point, v: Vector) -> Option<Self> {
if v.x==0.0 && v.y==0.0 && v.z==0.0{
return None;
}
Some(Line{p1:p, p2: p + v})
}
pub fn distance(&self, target: Point) -> f64 {
let norm=(self.p1-target)^(self.p2-target);
let length=(norm*norm).sqrt();
let chisl = (self.p1-target)^norm;
let chisl_num = (chisl * chisl).sqrt();
let distance=chisl_num / length;
return distance;

Тази функция не изглежда да минава базовия тест. Не съм 100% сигурен коя формула се опитваш да имплементираш, но ако е "Another vector formulation" от линка, не е съвсем това.

Пробвай тази формула: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Vector_formulation. Означена е ясно с етикети и графика, и вероятно ще е по-разбираема. Имай предвид, че:

  • вектора, който определя посоката на правата, n, е p2 - p1
  • вектора във тази формула се очаква да е нормализиран, тоест всяка координата да е разделена на дължината на целия вектор
  • дължината на един вектор е корен квадратен от сумата на квадратите на координатите: https://en.wikipedia.org/wiki/Norm_(mathematics)#Euclidean_norm

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

Ето един начин, по който тази формула изглежда да минава тестовете (с другите поправки):

pub fn distance(&self, target: Point) -> f64 {
    let norm=self.p1-self.p2;
    let length=(norm*norm).sqrt();
    let chisl = (self.p1-target)^norm;
    let chisl_num = (chisl * chisl).sqrt();
    let distance=chisl_num / length;

    return distance;
}

Аз бих използвал други имена, обаче:

pub fn distance(&self, target: Point) -> f64 {
    let direction = self.p1 - self.p2;
    let direction_length = (direction * direction).sqrt();
    let normal = (self.p1 - target) ^ direction;
    let normal_length = (normal * normal).sqrt();

    normal_length / direction_length
}
}
}
impl PartialEq for Line{
fn eq(&self, other: &Line)->bool{
let d1 = self.distance(other.p1);
let d2 = self.distance(other.p2);
if d1<std::f64::EPSILON && d2<std::f64::EPSILON{
return false;
}

Ако и двете разстояния до другите точки са 0, това значи, че и двете точки лежат на линията. Това значи, че линиите съвпадат, тоест са равни. Този код връща false в този случай.

Втори hint: Всички отрицателни числа са по-малки от std::f64::EPSILON.

Ако не е станало ясно, този return false трябва да е return true. Може и просто последния ред на функцията да бъде d1 < eps && d2 < eps, понеже този израз ще се оцени или до true или до false.

return true;
}
}

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

Compiling solution v0.1.0 (/tmp/d20190123-22631-1m5fz15/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 4.49s
     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 ... FAILED
test solution_test::test_line_equality_by_points_and_vectors ... FAILED
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 ... FAILED
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 { p1: Point { x: 0.0, y: 0.0, z: 0.0 }, p2: Point { x: 1.0, y: 1.0, z: 1.0 } })`,
 right: `Some(Line { p1: Point { x: 0.0, y: 0.0, z: 0.0 }, p2: Point { x: 1.0, y: -1.0, z: 1.0 } })`', tests/solution_test.rs:165:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

---- solution_test::test_line_equality_by_points stdout ----
thread 'solution_test::test_line_equality_by_points' panicked at 'assertion failed: `(left != right)`
  left: `Some(Line { p1: Point { x: 0.0, y: 0.0, z: 0.0 }, p2: Point { x: 0.1, y: 0.1, z: 0.1 } })`,
 right: `Some(Line { p1: Point { x: 0.0, y: 0.0, z: 0.0 }, p2: Point { x: 1.1, y: 2.1, z: 3.1 } })`', tests/solution_test.rs:215:5

---- solution_test::test_line_equality_by_points_and_vectors stdout ----
thread 'solution_test::test_line_equality_by_points_and_vectors' panicked at 'assertion failed: `(left != right)`
  left: `Some(Line { p1: Point { x: 0.0, y: 0.0, z: 0.0 }, p2: Point { x: 0.3, y: -0.1, z: 0.25 } })`,
 right: `Some(Line { p1: Point { x: 0.0, y: 0.0, z: 0.0 }, p2: Point { x: 0.1, y: -0.3, z: -0.25 } })`', tests/solution_test.rs:246:5

---- 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 { p1: Point { x: 0.0, y: 0.4, z: 0.0 }, p2: Point { x: 0.1, y: 0.2, z: 0.5 } })`,
 right: `Some(Line { p1: Point { x: 0.0, y: 0.4, z: 0.0 }, p2: Point { x: 0.2, y: 0.2, z: 0.5 } })`', tests/solution_test.rs:233:5

---- solution_test::test_number_vector_multiplication_with_precision stdout ----
thread 'solution_test::test_number_vector_multiplication_with_precision' panicked at 'assertion failed: `(left == right)`
  left: `Vector { x: -0.0000000000000000002220446049250313, y: -0.0000000000000000002220446049250313, z: -0.0000000000000000002220446049250313 }`,
 right: `Vector { x: 0.0, y: 0.0, z: 0.0 }`', tests/solution_test.rs:148: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_points
    solution_test::test_line_equality_by_points_and_vectors
    solution_test::test_line_equality_by_vectors
    solution_test::test_number_vector_multiplication_with_precision
    solution_test::test_point_distance

test result: FAILED. 9 passed; 6 failed; 0 ignored; 0 measured; 0 filtered out

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

История (2 версии и 8 коментара)

Даяна качи първо решение на 24.11.2018 20:25 (преди почти 7 години)

Даяна качи решение на 24.11.2018 22:45 (преди почти 7 години)

use std::ops::*;

Смислено. Обикновено е добре да use-ваме неща едно по едно за да не получим някой конфликт с имената, но за домашно, в което ще се дефинират оператори, това е удобно и ясно.

#[derive(Debug, Clone, Copy)]
pub struct Point {
x:f64,
y:f64,
z:f64,
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Point{x,y,z}
}
}
impl PartialEq for Point {
fn eq(&self, other: &Point)->bool{
let isx = if self.x < 0.0 && other.x < 0.0 {
(self.x.abs() - other.x.abs()).abs() < std::f64::EPSILON
} else if self.x >= 0.0 && other.x >= 0.0 {
(self.x - other.x).abs() < std::f64::EPSILON
} else {
false
};

Това може да се опрости така:

let isx = (self.x - other.x).abs() < std::f64::EPSILON;

Ако и двете са отрицателни, минуса ще обърне това отдясно и пак ще се "неутрализират". Ако и двете са положителни, същото. Ако едното е положително, а другото е отрицателно, ще получим абсолютната разлика между тях. която пак може да е достатъчно малка да ги считаме за равни.

let isy = if self.y < 0.0 && other.y < 0.0 {
(self.y.abs() - other.y.abs()).abs() < std::f64::EPSILON
} else if self.y >= 0.0 && other.y >= 0.0 {
(self.y - other.y).abs() < std::f64::EPSILON
} else {
false
};
let isz = if self.z < 0.0 && other.z < 0.0 {
(self.z.abs() - other.z.abs()).abs() < std::f64::EPSILON
} else if self.z >= 0.0 && other.z >= 0.0 {
(self.z - other.z).abs() < std::f64::EPSILON
} else {
false
};
return isx && isy && isz;
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vector {
x:f64,
y:f64,
z:f64,
}
impl Vector {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Vector{x,y,z}
}
}
impl PartialEq for Vector{
fn eq(&self, other: &Vector)->bool{
let isx = if self.x < 0.0 && other.x < 0.0 {
(self.x.abs() - other.x.abs()).abs() < std::f64::EPSILON
} else if self.x >= 0.0 && other.x >= 0.0 {
(self.x - other.x).abs() < std::f64::EPSILON
} else {
false
};
let isy = if self.y < 0.0 && other.y < 0.0 {
(self.y.abs() - other.y.abs()).abs() < std::f64::EPSILON
} else if self.y >= 0.0 && other.y >= 0.0 {
(self.y - other.y).abs() < std::f64::EPSILON
} else {
false
};
let isz = if self.z < 0.0 && other.z < 0.0 {
(self.z.abs() - other.z.abs()).abs() < std::f64::EPSILON
} else if self.z >= 0.0 && other.z >= 0.0 {
(self.z - other.z).abs() < std::f64::EPSILON
} else {
false
};
return isx && isy && isz;
}
}
impl Add<Point> for Vector{
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl Add<Vector> for Point{
type Output = Point;
fn add(self, other: Vector) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
z: self.z+other.z,
}
}
}
impl Add<Vector> for Vector{
type Output = Vector;
fn add(self, other: Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl Sub<Point> for Point{
type Output = Vector;
fn sub(self, other: Point) -> Vector {
Vector {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}
impl Mul<Vector> for f64{
type Output = Vector;
fn mul(self, other: Vector) -> Vector {
Vector {
x: self * other.x,
y: self * other.y,
z: self * other.z,
}
}
}
impl Mul<Vector> for Vector{
type Output = f64;
fn mul(self, other: Vector) -> f64 {
return self.x * other.x + self.y * other.y + self.z * other.z;
}
}
impl BitXor<Vector> for Vector{
type Output = Vector;
fn bitxor(self, other: Vector) -> Vector{
Vector{
x: self.y * other.z - self.z * other.y,
y: self.z * other.x - self.x * other.z,
z: self.x * other.y - self.y * other.x,
}
}
}
#[derive(Debug)]
pub struct Line {
p1: Point,
p2: Point,
}
impl Line {
pub fn from_pp(p1: Point, p2: Point) -> Option<Self> {
if p1==p2 {
return None;
}
Some(Line {p1:p1, p2:p2})
}
pub fn from_pv(p: Point, v: Vector) -> Option<Self> {
if v.x==0.0 && v.y==0.0 && v.z==0.0{
return None;
}
- Some(Line{p1:p, p2: Point::new (v.x, v.y, v.z)})
+ Some(Line{p1:p, p2: p + v})
}
pub fn distance(&self, target: Point) -> f64 {
let norm=(self.p1-target)^(self.p2-target);
- let resp=Vector{x:1.0/norm.x, y:1.0/norm.y, z:1.0/norm.z};
- let distance=((self.p1-target)^norm)*resp;
+ let length=(norm*norm).sqrt();
+ let chisl = (self.p1-target)^norm;
+ let chisl_num = (chisl * chisl).sqrt();
+ let distance=chisl_num / length;
return distance;

Ето един начин, по който тази формула изглежда да минава тестовете (с другите поправки):

pub fn distance(&self, target: Point) -> f64 {
    let norm=self.p1-self.p2;
    let length=(norm*norm).sqrt();
    let chisl = (self.p1-target)^norm;
    let chisl_num = (chisl * chisl).sqrt();
    let distance=chisl_num / length;

    return distance;
}

Аз бих използвал други имена, обаче:

pub fn distance(&self, target: Point) -> f64 {
    let direction = self.p1 - self.p2;
    let direction_length = (direction * direction).sqrt();
    let normal = (self.p1 - target) ^ direction;
    let normal_length = (normal * normal).sqrt();

    normal_length / direction_length
}
}
}
impl PartialEq for Line{
fn eq(&self, other: &Line)->bool{
let d1 = self.distance(other.p1);
let d2 = self.distance(other.p2);
if d1<std::f64::EPSILON && d2<std::f64::EPSILON{
return false;
}

Ако не е станало ясно, този return false трябва да е return true. Може и просто последния ред на функцията да бъде d1 < eps && d2 < eps, понеже този израз ще се оцени или до true или до false.

return true;
}
}