@leolee9086/string-metrics-dice
Version:
Sørensen-Dice 系数算法库
168 lines (149 loc) • 5.11 kB
JavaScript
/**
* @typedef {Object} DiceCoefficientOptions
* @property {number} [nGramSize=2] - n-gram大小,默认为2(bigram)
*/
/**
* @typedef {Object} DiceCoefficientResult
* @property {number} coefficient - Dice系数,范围[0,1]
* @property {number} distance - Dice距离,范围[0,1]
* @property {string} algorithm - 使用的算法名称
* @property {number} executionTime - 执行时间(毫秒)
*/
/**
* @typedef {Object} AlgorithmMetadata
* @property {string} name - 算法名称
* @property {string} version - 算法版本
* @property {Object} complexity - 复杂度信息
* @property {string} complexity.time - 时间复杂度
* @property {string} complexity.space - 空间复杂度
* @property {string[]} characteristics - 算法特征
* @property {string[]} bestFor - 适用场景
* @property {string} author - 作者
*/
/**
* @typedef {Object} StringDistanceAlgorithm
* @property {string} name - 算法名称
* @property {string} version - 算法版本
* @property {function(string, string, DiceCoefficientOptions): number} compute - 计算函数
* @property {AlgorithmMetadata} metadata - 算法元数据
*/
/**
* 默认的Dice系数计算选项
* @type {DiceCoefficientOptions}
*/
export const DEFAULT_OPTIONS = {
nGramSize: 2
};
/**
* 验证Dice系数计算选项
* @param {DiceCoefficientOptions} options - 选项对象
* @returns {DiceCoefficientOptions} 验证后的选项
*/
export function validateOptions(options = {}) {
const validated = { ...DEFAULT_OPTIONS, ...options };
if (typeof validated.nGramSize !== 'number' || validated.nGramSize < 1) {
throw new TypeError('nGramSize must be a positive number');
}
return validated;
}
/**
* 预处理字符串
* @param {string} str - 输入字符串
* @param {DiceCoefficientOptions} options - 选项
* @returns {string} 预处理后的字符串
*/
export function preprocessString(str, options) {
if (typeof str !== 'string') {
throw new TypeError('Input must be a string');
}
return str;
}
/**
* 生成n-gram集合
* @param {string} str - 输入字符串
* @param {number} nGramSize - n-gram大小
* @returns {Set<string>} n-gram集合
*/
export function generateNGrams(str, nGramSize) {
const ngrams = new Set();
if (str.length < nGramSize) {
return ngrams;
}
for (let i = 0; i <= str.length - nGramSize; i++) {
ngrams.add(str.slice(i, i + nGramSize));
}
return ngrams;
}
/**
* 8路循环展开生成n-gram集合
* @param {string} str - 输入字符串
* @param {number} nGramSize - n-gram大小
* @returns {Set<string>} n-gram集合
*/
export 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;
}
/**
* 计算两个集合的交集大小
* @param {Set<string>} set1 - 第一个集合
* @param {Set<string>} set2 - 第二个集合
* @returns {number} 交集大小
*/
export function computeIntersectionSize(set1, set2) {
let intersectionSize = 0;
// 选择较小的集合进行遍历
const [smallerSet, largerSet] = set1.size <= set2.size ? [set1, set2] : [set2, set1];
for (const item of smallerSet) {
if (largerSet.has(item)) {
intersectionSize++;
}
}
return intersectionSize;
}
/**
* 8路循环展开计算两个集合的交集大小
* @param {Set<string>} set1 - 第一个集合
* @param {Set<string>} set2 - 第二个集合
* @returns {number} 交集大小
*/
export 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;
}