不同國家有不同的貨幣流通,不同的貨幣也會有不同的價值。其實也不只貨幣,任何物品,無論實體的或是虛擬的都一樣,只要價值可以被衡量,就可以被等值換算。講到錢就傷感情,以蘋果和橘子來舉例好了,假設2顆蘋果等值於3粒橘子,那麼8顆蘋果就會等值於12粒橘子。
物品的價值
「2顆蘋果等值於3粒橘子」,我們可以說1顆蘋果的價值為#{{3 \over 2}}#粒橘子。所以#{{n}}#顆蘋果,就會等值於#{{n \times {3 \over 2}}}#粒橘子。
貨幣也是同樣的概念,假設1元#{{A}}#幣等值於1顆蘋果,再假設1元#{{B}}#幣等值於半顆蘋果,所以1元#{{A}}#幣等值於2元#{{B}}#幣,若有#{{n}}#元#{{A}}#幣就會等值於#{{2n}}#元#{{B}}#幣。
將以上的假設化為代數可列式如下:
#{{{
\text{若 n 元A幣的價值 $=$ m 元B幣的價值}
}}}#
#{{{
{{n \times \text{1元A幣的價值}} \over {m \times \text{1元B幣的價值}}} = 1
}}}#
#{{{
m = n \times {\text{1元A幣的價值} \over \text{1元B幣的價值}}
}}}#
匯率
匯率是一種貨幣兌換另一種貨幣的比率。假設現在還是一樣有#{{A}}#、#{{B}}#兩種貨幣,#{{A}}#幣對#{{B}}#幣的匯率就是:
#{{{
匯率 = {\text{1元A幣的價值} \over \text{1元B幣的價值}}
}}}#
#{{{
\text{若 n 元A幣的價值 $=$ m 元B幣的價值}
}}}#
#{{{
\begin{eqnarray}
m &=& n \times {\text{1元A幣的價值} \over \text{1元B幣的價值}} \nonumber \\
&=& n \times \text{A幣對B幣的匯率}
\end{eqnarray}
}}}#
物品的最小單位
假設2顆蘋果等值於30粒葡萄,我們可以說1顆蘋果的價值為15粒葡萄,所以#{{n}}#顆蘋果,就會等值於#{{15n}}#粒葡萄。
然而當我們實際在應用蘋果和葡萄的等值數量換算時(例如等值交換),我們想要讓蘋果總是完整的4顆一盒,而葡萄總是完整的30顆一串。在這樣的情況下,就要確保#{{n}}#是4的倍數,且#{{15n}}#是30的倍數,所以#{{n}}#要是什麼數的倍數才能夠同時符合這些條件呢?
我們可以先找出最小數量單位(4顆)的蘋果的價值,再找出最小數量單位(30顆)的葡萄的價值,取它們的最小公倍數(LCM, Least Common Multiple),就是「#{{n}}#要是什麼數的倍數」的那個數,稱它為#{{m}}#吧!#{{n}}#若是#{{m}}#的倍數,就可以同時符合蘋果和葡萄的最小數量單位。#{{m}}#的計算過程如下:
#{{{
\begin{eqnarray}
m &=& lcm(\text{蘋果的最小數量單位} \times \text{1顆蘋果的價值}, \nonumber \\
&\text{ }& \text{葡萄的最小單位} \times \text{1顆葡萄的價值}) \nonumber \\
&=& lcm({4 \times 1}, {30 \times {1 \over 15}}) \nonumber \\
&=& lcm(4, 2) \nonumber \\
&=& 4
\end{eqnarray}
}}}#
蘋果必須要是#{{m \over \text{1顆蘋果的價值}}}#的倍數;葡萄必須要是#{{m \over \text{1顆葡萄的價值}}}#的倍數。
貨幣也會有最小數量單位的問題,像是最小的美金就是1美分,相當於0.01元美金;而台幣是1元。
初步的程式實作
統整一下,要用程式做等值的貨幣或是物品的數量換算,我們會需要它們各自的價值和最小數量單位。此外,還會需要做最小公倍數的運算,所以如果您不熟悉寫程式算最小公倍數的話,可以查看這篇文章:
程式實作如下:
#[derive(Debug, Copy, Clone)]
pub struct Item {
pub value: f64,
pub unit_number: f64,
}
#[derive(Debug)]
pub struct MultipleFactorsOfAB {
pub a_factor: f64,
pub b_factor: f64,
}
pub fn evaluate_a_to_b(a: Item, a_number: f64, b: Item) -> Result<f64, &'static str> {
if !is_divisible(a_number, a.unit_number) {
return Err("The number of A is invalid.");
}
let b_number = a_number * a.value / b.value;
if !is_divisible(b_number, a.unit_number) {
return Err("The number of A is not quite valid.");
}
Ok(b_number)
}
pub fn get_multiple_factors_of_ab(a: Item, b: Item) -> MultipleFactorsOfAB {
let m = lcm(a.unit_number * a.value, b.unit_number * b.value);
MultipleFactorsOfAB {
a_factor: m / a.value,
b_factor: m / b.value,
}
}
fn is_divisible(dividend: f64, divisor: f64) -> bool {
let quotient = dividend / divisor;
quotient == quotient.round()
}
fn lcm(a: f64, b: f64) -> f64 {
a * b / gcd(a, b)
}
fn gcd(mut a: f64, mut b: f64) -> f64 {
if b > a {
core::mem::swap(&mut a, &mut b);
}
let mut m = a % b;
while m != 0.0 {
a = b;
b = m;
m = a % b;
}
b
}
fn main() {
let apples = Item {
value: 1.0,
unit_number: 4.0,
};
let grapes = Item {
value: 1.0 / 15.0,
unit_number: 30.0,
};
println!("{:?}", get_multiple_factors_of_ab(apples, grapes));
println!("{:?}", evaluate_a_to_b(apples, 4.0, grapes));
}
public class ItemExchange {
public static class Item {
public double value;
public double unitNumber;
public Item(final double value, final double unitNumber) {
this.value = value;
this.unitNumber = unitNumber;
}
}
public static class MultipleFactorsOfAB {
public double aFactor;
public double bFactor;
public MultipleFactorsOfAB(final double aFactor, final double bFactor) {
this.aFactor = aFactor;
this.bFactor = bFactor;
}
@Override
public String toString() {
return "aFactor=" + aFactor + ", bFactor=" + bFactor;
}
}
public static double evaluateAtoB(final Item a, final double aNumber, final Item b) {
if (!isDivisible(aNumber, a.unitNumber)) {
throw new IllegalArgumentException("The number of A is invalid.");
}
final double bNumber = aNumber * a.value / b.value;
if (!isDivisible(bNumber, b.unitNumber)) {
throw new IllegalArgumentException("The number of A is not quite valid.");
}
return bNumber;
}
public static MultipleFactorsOfAB getMultipleFactorsOfAB(final Item a, final Item b) {
final double m = lcm(a.unitNumber * a.value, b.unitNumber * b.value);
return new MultipleFactorsOfAB(m / a.value, m / b.value);
}
private static boolean isDivisible(final double dividend, final double divisor) {
final double quotient = dividend / divisor;
return quotient == Math.round(quotient);
}
private static double lcm(double a, double b) {
return a * b / gcd(a, b);
}
private static double gcd(double a, double b) {
if (b > a) {
final double c = b;
b = a;
a = c;
}
double m = a % b;
while (m != 0) {
a = b;
b = m;
m = a % b;
}
return b;
}
public static void main(final String[] args) {
final Item apples = new Item(1.0, 4.0);
final Item grapes = new Item(1.0 / 15.0, 30.0);
System.out.println(getMultipleFactorsOfAB(apples, grapes));
System.out.println(evaluateAtoB(apples, 4.0, grapes));
}
}
function evaluateAtoB(a, aNumber, b) {
if (!isDivisible(aNumber, a.unitNumber)) {
throw new Error("The number of A is invalid.");
}
const bNumber = aNumber * a.value / b.value;
if (!isDivisible(bNumber, b.unitNumber)) {
throw new Error("The number of A is not quite valid.");
}
return bNumber;
}
function getMultipleFactorsOfAB(a, b) {
const m = lcm(a.unitNumber * a.value, b.unitNumber * b.value);
return {
aFactor: m / a.value,
bFactor: m / b.value
};
}
function isDivisible(dividend, divisor) {
const quotient = dividend / divisor;
return quotient == Math.round(quotient);
}
function lcm(a, b) {
return a * b / gcd(a, b);
}
function gcd(a, b) {
if (b > a) {
const c = b;
b = a;
a = c;
}
let m = a % b;
while (m !== 0) {
a = b;
b = m;
m = a % b;
}
return b;
}
const apples = {
value: 1.0,
unitNumber: 4.0,
};
const grapes = {
value: 1.0 / 15.0,
unitNumber: 30.0,
};
console.log(getMultipleFactorsOfAB(apples, grapes));
console.log(evaluateAtoB(apples, 4.0, grapes));
雖然以上程式的執行結果是正確的,但由於是使用浮點數來運算的關係,會有精準度問題,當輸入別的參數數值時可能會得到錯誤的結果。可以參考這篇文章來處理浮點數的誤差:
最終的程式實作
#[derive(Debug, Copy, Clone)]
pub struct Item {
pub value: f64,
pub unit_number: f64,
}
#[derive(Debug)]
pub struct MultipleFactorsOfAB {
pub a_factor: f64,
pub b_factor: f64,
}
pub fn evaluate_a_to_b(a: Item, a_number: f64, b: Item) -> Result<f64, &'static str> {
if !is_divisible(a_number, a.unit_number) {
return Err("The number of A is invalid.");
}
let b_number = a_number * a.value / b.value;
if !is_divisible(b_number, a.unit_number) {
return Err("The number of A is not quite valid.");
}
Ok(b_number)
}
pub fn get_multiple_factors_of_ab(a: Item, b: Item) -> MultipleFactorsOfAB {
let m = lcm(a.unit_number * a.value, b.unit_number * b.value);
MultipleFactorsOfAB {
a_factor: m / a.value,
b_factor: m / b.value,
}
}
fn is_divisible(dividend: f64, divisor: f64) -> bool {
if divisor.abs() < f64::MIN_POSITIVE {
dividend.abs() < f64::MIN_POSITIVE
} else {
let quotient = dividend / divisor;
is_equal(quotient, quotient.round())
}
}
fn lcm(a: f64, b: f64) -> f64 {
a * b / gcd(a, b)
}
fn gcd(mut a: f64, mut b: f64) -> f64 {
if b > a {
core::mem::swap(&mut a, &mut b);
}
let mut m = calculate_remainder(a, b);
while m != 0.0 {
a = b;
b = m;
m = calculate_remainder(a, b);
}
b
}
fn calculate_remainder(dividend: f64, divisor: f64) -> f64 {
let quotient = dividend / divisor;
let quotient_ceil = quotient.ceil();
let floor = if is_equal(quotient, quotient_ceil) {
quotient_ceil
} else {
quotient.floor()
};
dividend - (floor * divisor)
}
fn is_equal(a: f64, b: f64) -> bool {
if a == b {
return true;
} else {
let a_abs = a.abs();
let b_abs = b.abs();
let d_abs = (a - b).abs();
d_abs < a_abs.min(b_abs) * f64::EPSILON * 8.0
}
}
fn main() {
let apples = Item {
value: 1.0,
unit_number: 4.0,
};
let grapes = Item {
value: 1.0 / 15.0,
unit_number: 30.0,
};
println!("{:?}", get_multiple_factors_of_ab(apples, grapes));
println!("{:?}", evaluate_a_to_b(apples, 4.0, grapes));
}
public class ItemExchange {
public static class Item {
public double value;
public double unitNumber;
public Item(final double value, final double unitNumber) {
this.value = value;
this.unitNumber = unitNumber;
}
}
public static class MultipleFactorsOfAB {
public double aFactor;
public double bFactor;
public MultipleFactorsOfAB(final double aFactor, final double bFactor) {
this.aFactor = aFactor;
this.bFactor = bFactor;
}
@Override
public String toString() {
return "aFactor=" + aFactor + ", bFactor=" + bFactor;
}
}
public static double evaluateAtoB(final Item a, final double aNumber, final Item b) {
if (!isDivisible(aNumber, a.unitNumber)) {
throw new IllegalArgumentException("The number of A is invalid.");
}
final double bNumber = aNumber * a.value / b.value;
if (!isDivisible(bNumber, b.unitNumber)) {
throw new IllegalArgumentException("The number of A is not quite valid.");
}
return bNumber;
}
public static MultipleFactorsOfAB getMultipleFactorsOfAB(final Item a, final Item b) {
final double m = lcm(a.unitNumber * a.value, b.unitNumber * b.value);
return new MultipleFactorsOfAB(m / a.value, m / b.value);
}
private static boolean isDivisible(final double dividend, final double divisor) {
final double DBL_MIN_POSITIVE = Math.ulp(0.0);
if (Math.abs(divisor) < DBL_MIN_POSITIVE) {
return Math.abs(dividend) < DBL_MIN_POSITIVE;
} else {
final double quotient = dividend / divisor;
return isEqual(quotient, Math.round(quotient));
}
}
private static double lcm(double a, double b) {
return a * b / gcd(a, b);
}
private static double gcd(double a, double b) {
if (b > a) {
final double c = b;
b = a;
a = c;
}
double m = calculateRemainder(a, b);
while (m != 0) {
a = b;
b = m;
m = calculateRemainder(a, b);
}
return b;
}
private static double calculateRemainder(final double dividend, final double divisor) {
final double quotient = dividend / divisor;
final double quotientCeil = Math.ceil(quotient);
final double floor;
if (isEqual(quotient, quotientCeil)) {
floor = quotientCeil;
} else {
floor = Math.floor(quotient);
}
return Math.abs(dividend) - Math.abs(floor * divisor);
}
private static boolean isEqual(final double a, final double b) {
if (a == b) {
return true;
} else {
final double aAbs = Math.abs(a);
final double bAbs = Math.abs(b);
final double dAbs = Math.abs(a - b);
return dAbs < Math.min(aAbs, bAbs) * Math.ulp(1.0) * 8.0;
}
}
public static void main(final String[] args) {
final Item apples = new Item(1.0, 4.0);
final Item grapes = new Item(1.0 / 15.0, 30.0);
System.out.println(getMultipleFactorsOfAB(apples, grapes));
System.out.println(evaluateAtoB(apples, 4.0, grapes));
}
}
function evaluateAtoB(a, aNumber, b) {
if (!isDivisible(aNumber, a.unitNumber)) {
throw new Error("The number of A is invalid.");
}
const bNumber = aNumber * a.value / b.value;
if (!isDivisible(bNumber, b.unitNumber)) {
throw new Error("The number of A is not quite valid.");
}
return bNumber;
}
function getMultipleFactorsOfAB(a, b) {
const m = lcm(a.unitNumber * a.value, b.unitNumber * b.value);
return {
aFactor: m / a.value,
bFactor: m / b.value
};
}
function isDivisible(dividend, divisor) {
const DBL_MIN_POSITIVE = 4.9406564584124654e-324;
if (Math.abs(divisor) < DBL_MIN_POSITIVE) {
return Math.abs(dividend) < DBL_MIN_POSITIVE;
} else {
const quotient = dividend / divisor;
return isEqual(quotient, Math.round(quotient));
}
}
function lcm(a, b) {
return a * b / gcd(a, b);
}
function gcd(a, b) {
if (b > a) {
const c = b;
b = a;
a = c;
}
let m = calculateRemainder(a, b);
while (m !== 0) {
a = b;
b = m;
m = calculateRemainder(a, b);
}
return b;
}
function calculateRemainder(dividend, divisor) {
const quotient = dividend / divisor;
const quotientCeil = Math.ceil(quotient);
let floor;
if (isEqual(quotient, quotientCeil)) {
floor = quotientCeil;
} else {
floor = Math.floor(quotient);
}
return Math.abs(dividend) - Math.abs(floor * divisor);
}
function isEqual(a, b) {
if (a === b) {
true
} else {
const aAbs = Math.abs(a);
const bAbs = Math.abs(b);
const dAbs = Math.abs(a - b);
return dAbs < Math.min(aAbs, bAbs) * Number.EPSILON * 8.0;
}
}
const apples = {
value: 1.0,
unitNumber: 4.0,
};
const grapes = {
value: 1.0 / 15.0,
unitNumber: 30.0,
};
console.log(getMultipleFactorsOfAB(apples, grapes));
console.log(evaluateAtoB(apples, 4.0, grapes));