@leolee9086/string-metrics-dice
Version:
Sørensen-Dice 系数算法库
123 lines (118 loc) • 4.1 kB
JavaScript
import { validateOptions, preprocessString } from './schema.js';
/**
* 8路循环展开生成n-gram集合
* @param {string} str - 输入字符串
* @param {number} nGramSize - n-gram大小
* @returns {Set<string>} n-gram集合
*/
function generateNGramsUnroll8(str, nGramSize) {
const ngrams = new Set();
const len = str.length - nGramSize + 1;
let i = 0;
// 8路展开
for (; i <= len - 8; i += 8) {
ngrams.add(str.slice(i, i + nGramSize));
ngrams.add(str.slice(i + 1, i + 1 + nGramSize));
ngrams.add(str.slice(i + 2, i + 2 + nGramSize));
ngrams.add(str.slice(i + 3, i + 3 + nGramSize));
ngrams.add(str.slice(i + 4, i + 4 + nGramSize));
ngrams.add(str.slice(i + 5, i + 5 + nGramSize));
ngrams.add(str.slice(i + 6, i + 6 + nGramSize));
ngrams.add(str.slice(i + 7, i + 7 + nGramSize));
}
// 处理剩余部分
for (; i < len; i++) {
ngrams.add(str.slice(i, i + nGramSize));
}
return ngrams;
}
/**
* 8路循环展开计算两个集合的交集大小
* @param {Set<string>} set1 - 第一个集合
* @param {Set<string>} set2 - 第二个集合
* @returns {number} 交集大小
*/
function computeIntersectionSizeUnroll8(set1, set2) {
let intersectionSize = 0;
const [smallerSet, largerSet] = set1.size <= set2.size ? [set1, set2] : [set2, set1];
const arr = Array.from(smallerSet);
const len = arr.length;
let i = 0;
// 8路展开
for (; i <= len - 8; i += 8) {
if (largerSet.has(arr[i])) intersectionSize++;
if (largerSet.has(arr[i + 1])) intersectionSize++;
if (largerSet.has(arr[i + 2])) intersectionSize++;
if (largerSet.has(arr[i + 3])) intersectionSize++;
if (largerSet.has(arr[i + 4])) intersectionSize++;
if (largerSet.has(arr[i + 5])) intersectionSize++;
if (largerSet.has(arr[i + 6])) intersectionSize++;
if (largerSet.has(arr[i + 7])) intersectionSize++;
}
// 处理剩余部分
for (; i < len; i++) {
if (largerSet.has(arr[i])) intersectionSize++;
}
return intersectionSize;
}
/**
* 8路循环展开Sørensen-Dice系数算法实现
*
* 算法原理同basic.js,但n-gram生成和交集计算采用8路循环展开
*
* @param {string} str1 - 第一个字符串
* @param {string} str2 - 第二个字符串
* @param {import('./schema.js').DiceCoefficientOptions} options - 计算选项
* @returns {number} Dice系数,范围[0,1]
*/
export function computeDiceCoefficientUnroll8(str1, str2, options = {}) {
const validatedOptions = validateOptions(options);
// 预处理字符串
const processedStr1 = preprocessString(str1, validatedOptions);
const processedStr2 = preprocessString(str2, validatedOptions);
// 生成n-gram集合(8路展开)
const ngrams1 = generateNGramsUnroll8(processedStr1, validatedOptions.nGramSize);
const ngrams2 = generateNGramsUnroll8(processedStr2, validatedOptions.nGramSize);
// 计算交集大小(8路展开)
const intersectionSize = computeIntersectionSizeUnroll8(ngrams1, ngrams2);
// 计算Dice系数
const totalSize = ngrams1.size + ngrams2.size;
if (totalSize === 0) {
return 0;
}
return (2 * intersectionSize) / totalSize;
}
/**
* 计算Dice距离(1 - Dice系数)
* @param {string} str1 - 第一个字符串
* @param {string} str2 - 第二个字符串
* @param {import('./schema.js').DiceCoefficientOptions} options - 计算选项
* @returns {number} Dice距离,范围[0,1]
*/
export function computeDiceDistanceUnroll8(str1, str2, options = {}) {
const coefficient = computeDiceCoefficientUnroll8(str1, str2, options);
return 1 - coefficient;
}
/**
* 算法元数据
*/
export const metadata = {
name: 'Unroll8 Sørensen-Dice',
version: '1.0.0',
complexity: {
time: 'O(n)',
space: 'O(n)'
},
characteristics: [
'8路循环展开',
'批量处理',
'适合大数据',
'高性能优化'
],
bestFor: [
'长字符串比较',
'高性能要求',
'大规模数据处理'
],
author: '织'
};