在下面的代碼中,我將各種口味的冰淇淋(巧克力、草莓、香草和那不勒斯)混合在一起,以產生一種新的、前所未見的冰淇淋口味。*
風味由一個陣串列示,其中第一個元素只是一個字串,即風味的名稱。
第二個元素是從眾多0到100表示香草成分,第三巧克力成分和第四草莓組件。
混合是通過將所有輸入風味(陣列)平均在一起來執行的。
混合后,我嘗試確定新混合物與哪種口味最相似。這是通過計算神秘冰淇淋和已知口味的絕對差異之和來完成的。總和越小,差異越小,相似度越大。
在此特定示例中,混合物是 6 份草莓冰淇淋和 1 份其他口味的冰淇淋。可以預見,草莓被計算為最相似,其次是那不勒斯,因為它本身就是混合物。
這是對混合物進行逆向工程的好方法,但我想更進一步。我想確定混合物中每種口味的精確比例。
在這個例子中,它會如上所述:6草莓、1香草、1巧克力、1那不勒斯。
當然,可能有很多(無限的?)方法來提出給定的混合物。但我正在尋找最簡約的可能性。
例如,1部分新城加1部分草莓與4部分草莓加3部分其他口味相同。但前者更為簡約。
我將如何預測混合物的創建方式?
我不知道這是什么技術術語。
const mixture = mixIcecreams([
['vanilla', 100, 0, 0],
['chocolate', 0, 100, 0],
['neapolitan', 33, 33, 33],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
]);
console.log(mixture);
const distances = calculateDistances(mixture, [
['vanilla', 100, 0, 0],
['chocolate', 0, 100, 0],
['strawberry', 0, 0, 100],
['neapolitan', 33, 33, 33],
]);
console.log('Distances:');
console.log(distances);
console.log(
`The icecream named "${mixture[0]}" is most similar to "${distances[0][0]}" icecream.`
);
// Calculate the "distance" between a "target" vector and "sources" vectors.
// Smaller distance means more similarity.
function calculateDistances(target, sources) {
return (
sources
.map((source) => [
// First element is the label.
source[0],
target.reduce(
(distance, value, i) =>
// Avoid doing math with the first element (the label).
i === 0 ? distance : distance Math.abs(source[i] - value),
0
),
])
// Sort by shortest distance (most similar).
.sort((a, b) => a[1] - b[1])
);
}
function mixIcecreams(icecreams) {
return icecreams.reduce(
(mixture, icecream, i) => {
icecream.forEach((value, j) => j !== 0 && (mixture[j] = value));
if (i === icecreams.length - 1) {
return mixture.map((value, j) =>
// Ignore the first element, it's just a label.
j === 0 ? value : value / icecreams.length
);
}
return mixture;
},
Array.from({ length: icecreams[0].length }, (_, i) =>
i === 0 ? 'mixture' : 0
)
);
}
*專利申請中。
uj5u.com熱心網友回復:
如果我正確理解你的問題,在數學術語中,你似乎需要一個欠定方程組的解,在最小二乘意義上。
我提出了一個可以改進的快速解決方案。
如果有趣,我可以進一步解釋。
編輯:我添加了一個簡單的整數近似,以找到最接近百分比的整數解。
const mixture = mixIcecreams([
['vanilla', 100, 0, 0],
['chocolate', 0, 100, 0],
['neapolitan', 33, 33, 33],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
['strawberry', 0, 0, 100],
]);
console.log(mixture);
const distances = calculateDistances(mixture, [
['vanilla', 100, 0, 0],
['chocolate', 0, 100, 0],
['strawberry', 0, 0, 100],
['neapolitan', 33, 33, 33],
]);
console.log('Distances:');
console.log(distances);
console.log(
`The icecream named "${mixture[0]}" is most similar to "${distances[0][0]}" icecream.`
);
const probableMixture = mostProbableMixture(mixture, [
['vanilla', 100, 0, 0],
['chocolate', 0, 100, 0],
['strawberry', 0, 0, 100],
['neapolitan', 33, 33, 33],
]
);
console.log('most likely mixture, percent', probableMixture)
const im = integerMix(probableMixture, 100);
// the second argument, nMax, is the maximum number allowed from one basic flavor
// the larger nMax, the closer you may get to the exact mixture
console.log('integer mixture', im)
const rm = repeatIntegerMix(im, [
['vanilla', 100, 0, 0],
['chocolate', 0, 100, 0],
['strawberry', 0, 0, 100],
['neapolitan', 33, 33, 33],
]);
console.log('verify mixture', mixIcecreams(rm))
// Calculate the "distance" between a "target" vector and "sources" vectors.
// Smaller distance means more similarity.
function calculateDistances(target, sources) {
return (
sources
.map((source) => [
// First element is the label.
source[0],
target.reduce(
(distance, value, i) =>
// Avoid doing math with the first element (the label).
i === 0 ? distance : distance Math.abs(source[i] - value),
0
),
])
// Sort by shortest distance (most similar).
.sort((a, b) => a[1] - b[1])
);
}
function mixIcecreams(icecreams) {
return icecreams.reduce(
(mixture, icecream, i) => {
icecream.forEach((value, j) => j !== 0 && (mixture[j] = value));
if (i === icecreams.length - 1) {
return mixture.map((value, j) =>
// Ignore the first element, it's just a label.
j === 0 ? value : value / icecreams.length
);
}
return mixture;
},
Array.from({ length: icecreams[0].length }, (_, i) =>
i === 0 ? 'mixture' : 0
)
);
}
function mostProbableMixture(mixture, baseFlavors){
const nVars = baseFlavors.length,
nEq = mixture.length - 1,
At = baseFlavors.map(flavor=>flavor.slice(1)),
b = mixture.slice(1),
AAt = Array(nEq).fill(0).map(z=>Array(nEq));
//compute A*At
for(let i = 0; i < nEq; i ){
for(let j = 0; j < nEq; j ){
AAt[i][j] = 0;
for(let k = 0; k < nVars; k ){
AAt[i][j] = At[k][i]*At[k][j];
}
}
}
// normalize rows
for(let i = 0; i < nEq; i ){
let maxRow = Math.abs(b[i]);
for(let j = 0; j < nEq; j ){
maxRow = Math.max(maxRow, Math.abs(AAt[i][j]));
}
for(let j = 0; j < nEq; j ){
AAt[i][j] = AAt[i][j] /maxRow;
}
b[i] = b[i]/maxRow;
}
// Solve (for t) A * At * t = b
// Gaussian elimination; diagonal dominance, no pivoting
for(let j = 0; j < nEq-1; j ){
for(let i = j 1; i < nEq; i ){
let f = AAt[i][j] / AAt[j][j];
for(let k = j 1; k < nEq; k ){
AAt[i][k] -= f * AAt[j][k];
}
b[i] -= f*b[j];
}
}
const t = Array(nEq).fill(0);
t[nEq-1] = b[nEq-1]/AAt[nEq-1][nEq-1];
for(let i = nEq-2; i >= 0; i--){
let s = b[i];
for(let j = i 1; j<nEq; j ){
s -= AAt[i][j]*t[j];
}
t[i] = s/AAt[i][i];
}
// the solution is y = At * t
const y = Array(nVars).fill(0);
let sy = 0;
for(let i = 0; i < nVars; i ){
for(let j = 0; j < nEq; j ){
y[i] = At[i][j] * t[j];
}
sy = y[i];
}
for(let i = 0; i < nVars; i ){
y[i] = y[i]/sy*100;
}
return y.map((yi, i)=>[baseFlavors[i][0], yi]);
}
function integerMix(floatMix, nMax){
const y = floatMix.map(a=>a[1]),
maxY = y.reduce((m,v)=>Math.max(m,v));
let minAlpha = 0, minD = 1e6;
for(let alpha = 1/maxY; alpha <= nMax/maxY; alpha =(nMax-1)/10000/maxY){
//needs refining!!
const sdif = y.map(v=>Math.pow(Math.round(v*alpha)-v*alpha, 2));
const d2 = sdif.reduce((s,x)=>s x);
if(d2 < minD){
minD = d2;
minAlpha = alpha;
}
}
return floatMix.map(([name, f]) => [name, Math.round(minAlpha*f)]);
}
function repeatIntegerMix(integerMix, baseFlavors){
return integerMix.flatMap(([name, n], i)=> Array.from({length: n}, e=>baseFlavors[i].slice(0)));
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/374606.html
標籤:javascript 数学 向量 数学优化
上一篇:python中“complex(*(1,2))”中的星號符號的目的是什么,沒有它會回傳錯誤?[復制]
下一篇:3D圖形中主要使用哪種旋轉技術
