如何阅读此基数树结构以确定下一个字符串概率

How can I read this Radix Tree structure to determine next-string probability?

本文关键字:下一个 字符串 概率 结构 何阅读      更新时间:2023-11-10

在JavaScript中,我试图接受给定的用户输入并猜测3个最有可能完成用户当前(不完整(输入的单词的单词。猜测基于用户过去的输入。我正在这里,在这个JSFiddle中研究这个。

我为记录用户过去的输入而构建的结构是修改后的基数树(又名Patricia Trie(:

输入:"hey">

{
    "h": {
        "value": "h",
        "count": 1,
        "followables": {
            "e": {
                "value": "e",
                "count": 1,
                "followables": {
                    "y": {
                        "value": "y",
                        "count": 1,
                        "followables": {}
                    }
                }
            }
        }
    }
}

这个数据结构是完美构建的,我认为它是实现所述目标的最佳结构。我的问题是读取基数树数据以定义给定输入的 3 个最可能的单词的功能。例如,在上面的数据中,如果用户输入" h",猜测函数应该返回如下对象:

guess : {
   1 : "hey",
   2 : "",
   3 : ""
}

所以这是我的代码/进度:

学习 - 获取完成的输入字符串并将组合组织到基数树中 ( brain (:

function learn(message, brain) {
    if (message.length == 0) return {}; // or do something else
    var ch = message[0]; // get the first character
    if (!brain[ch]) { // create new node when not exists
        brain[ch] = {
            value: ch,
            count: 1,
            followables: {}
        };
    } else { // increment count when exist
        brain[ch].count += 1;
    }
    var substr = message.substring(1); // remove first character
    if (substr) { // do it for the remaining substring
        brain[ch].followables = learn(substr, brain[ch].followables);
    } else {
        renderData();
    }
    return brain;
}

这一切都做对了。不幸的是,下一个代码,旨在读取数据并猜测用户正在键入的单词,并不好。我对一个对我来说非常复杂的功能有问题。我把它分成了几个小函数,因为我已经知道这是最佳实践,但恐怕我弄得一团糟,可能更简单:

猜测 - 获取"学习"的字符串数据,并猜测用户可能正在键入哪个单词:

function guess(progress, brain) {
    console.log("Guessing based on: " + progress);
    var guesses = {
        0: "",
        1: "",
        2: ""
    }
    var firstChar = progress[0]; 
    if (brain[firstChar]) {
        var step = brain[firstChar];
        for (var i = 0; i < progress.length; i++) {
            var char = progress[i];
            if (step.followables[char]) {
                step = step.followables[char];
                if (i == progress.length) {
                    var guesses = nextStrings(step.followables);
                    renderGuesses(guesses);
                }
            } else {
                renderGuesses(guesses);
            }
        }
    } else {
        renderGuesses(guesses);
    }
}
function renderGuesses(guesses) {
    console.log(guesses);
    $('#guess-1').text(guesses[0]);
    $('#guess-2').text(guesses[1]);
    $('#guess-3').text(guesses[2]);
}
function nextStrings(followables) {
    console.log('Searching for next string...');
    var results;
    if (followables.length > 0) {
        results = chooseRoutes(followables);
    } else {
        results = {
            0: "",
            1: "",
            2: ""
        }
    }
    console.log(result);
    return result;
}
function chooseRoutes(followables) {
    var results = {
        0: {
            value: "",
            count: 0
        },
        1: {
            value: "",
            count: 0
        },
        2: {
            value: "",
            count: 0
        }
    };
    for (var i = 0; i < followables.length; i++) {
        var count = followables[i].count;
        if (count > results[0].count) {
            results[0].value = followStr(followables[i], "");
        } else if (count > results[1].count) {
            results[1].value = followStr(followables[i], "");
        } else if (count > results[2].count) {
            results[2].value = followStr(followables[i], "");
        }
    }
    console.log(results);
    return results;
}
function followStr(followables, str) {
    var guess = {
        value: "",
        count: 0
    };
    for (var i = 0; i < followables.length; i++) {
        if (followables[i].count > guess.count) {
            guess = followables[i];
        }
    }
    followables = guess.followables;
    if (guess.value != " ") {
        str += guess;
        followStr(followables, str);
    } else {
        console.log(str);
        return str;
    }
}

旁注 - 虽然在字典上进行模糊字符串搜索是一种更常见的方法,但学习方法是根据用户的写作/消息传递风格定制猜测并支持用户非标准词汇("heyy"、"sup"、":P"、"lol"(的好方法——这些猜测的结果可以与标准字典结果相结合(并优先于标准字典结果(。

您用于字典的结构不正确,它应该包含对象数组。例如,输入以下单词后:

hi
hi
hi
hi
hi
hey
hello
hella

结构应为:

history: [{
    letter: "h",
    count: 8,
    followables: [{
        letter: "e",
        count: 3,
        followables: [{
            letter: "y",
            count: 1,
            followables: []
        }, {
            letter: "l",
            count: 2,
            followables: [{
                letter: "l",
                count: 2,
                followables: [{
                    letter: "o",
                    count: 1,
                    followables: []
                }, {
                    letter: "a",
                    count: 1,
                    followables: []
                }]
            }]
        }]
    }, {
        letter: "i",
        count: 5,
        followables: []
    }]
}]

您创建和存储历史记录的方式(我会使用 localStorage(对我来说并不有趣。重点是递归函数,这些函数深入挖掘树内部以获取建议。这个得到给定单词的最终followables

findChildren: function (node, depth) {
    /* Traverse history for current word, there is only one path */
    for (k in node) {
        if (node[k].letter == app.progress[depth]) {
            if (depth + 1 == app.progress.length) {
                /* Found it, hope it has followables */
                return node[k].followables;
            } else {
                /* Must go deeper... */
                return app.findChildren(node[k].followables, depth + 1);
            };
        };
    };
    /* No results */
    return false;
}

第二个(getWord(创建建议:

countWordsFromNode: function (node) {
    for (i in node) {
        if (node[i].followables.length) {
            app.countWordsFromNode(node[i].followables);
        } else {
            app.totalInNode++;
        };
    };
},
getWord: function (node, limit) {
    /* First sort by count */
    var sorted = node.sort(function (n1, n2) {
        return n2.count - n1.count;
    });
    for (k in sorted) {
        app.guesses[app.totalFound].word += sorted[k].letter;
        if (sorted[k].followables.length) {
            app.totalInNode = 0;
            app.countWordsFromNode(sorted[k].followables);
            for (m = 1; m < app.totalInNode; m++) {
                if ((app.totalFound + m) < limit) {
                    app.guesses[app.totalFound + m].word += sorted[k].letter;
                };
            };
            app.getWord(sorted[k].followables, limit);
        } else {
            /* End of word */
            app.totalFound++;
        };
        if (app.totalFound >= limit) {
            /* Found all suggestions */
            break;
        };
    };
}

您可以在此小提琴中看到详细信息,我不得不删除您的一些代码。在任何地方集成都很容易,例如您可以设置其他建议字段,当前为:

guesses: [
    {
        element: $('#guess-1'),
        word: ''
    },
    {
        element: $('#guess-2'),
        word: ''
    },
    {
        element: $('#guess-3'),
        word: ''
    }
]

编辑:修复了在右侧添加字母的错误。