背景介绍
Vue2.x项目上有这样的要求,上传指定比例的图片,然后给一个提示给用户,比如图片是375x250,最终比例就是5:3,因为375*250会在很多页面用到,就通过混入复用了,但是,提示还是直接黏贴原型图里的。
虽然后续改了大小,尝到了封装的甜头,可比例却不是自动更新的...
实现一
首先想到的就是穷举了
function getRaioStr(...rest: number[]) {
const list = [...rest];
const min = Math.min(...list);
for (let i = 1; i <= min; i++) {
const allow = list.every((item) => item / i === parseInt(item / i + ''));
if (allow) {
list.forEach((item, index, arr) => (arr[index] = item / i));
}
}
const str = list.join(':');
return str;
}
明显计算了很多次
function getRaioStr(...rest: number[]) {
let list = [...rest];
const min = Math.sqrt(Math.min(...list));
for (let i = 2; i <= min; i++) {
const tempList: number[] = [];
const allow = list.every((item) => {
let number = item / i;
if (number === parseInt(number + '')) {
tempList.push(number);
return true;
}
return false;
});
if (allow) {
list = tempList;
}
}
const str = list.join(':');
return str;
}
看似正确,其实有个致命的问题
getRaioStr(8, 4)
// 4:2
getRaioStr(375, 250));
//75:50
出现一个公约数后,要重置循环标记和最小值
function getRaioStr(...rest: number[]) {
let list = [...rest];
let min = Math.sqrt(Math.min(...list));
for (let i = 2; i <= min; i++) {
const tempList: number[] = [];
const allow = list.every((item) => {
let number = item / i;
if (number === parseInt(number + '')) {
tempList.push(number);
return true;
}
return false;
});
if (allow) {
i = 1;
min = Math.sqrt(Math.min(...list));
list = tempList;
}
}
const str = list.join(':');
return str;
}
也可以从大往小循环
辗转相除法
辗转相除法又名广义欧几里得除法,是用来求解两个数的最大公约数的最佳算法之一。
算法原理:若a除以b的余数为r , 则有 (a , b) = ( b ,r ) ((a,b)表示a和b的最大公约数)
function getRaioStr(...restList: number[]) {
let arrList = [...restList];
function gsy(m: number, n: number): number {
return m % n == 0 ? n : gsy(n, m % n);
}
while (arrList.length > 1) {
arrList.push(gsy(arrList.pop(), arrList.pop()));
}
const num = arrList[0];
const result = restList
.map((item, i, arr) => (arr[i] = item / num))
.join(':');
return result;
}
网友评论