用于 n * (n - 1) / 2 算法的 MySQL 架构
Posted
技术标签:
【中文标题】用于 n * (n - 1) / 2 算法的 MySQL 架构【英文标题】:MySQL architecture for n * (n - 1) / 2 algorithm 【发布时间】:2012-09-22 16:44:51 【问题描述】:我目前正在开发一个网站,用户可以在该网站上根据属性(年龄、身高、城镇、教育等)搜索其他用户。我现在想在用户配置文件之间实现某种评级。评级是通过其自己的算法根据 2 个给定配置文件之间的相似性计算的。例如,用户 A 对用户 B 的评级“匹配评级”为 85,对用户 C 的评级“匹配评级”为 79。 B 和 C 的评分为 94,依此类推....
用户应该能够搜索某些属性并按评级过滤结果。
由于评分因个人资料而异,并且还取决于进行搜索的用户,所以我不能简单地在我的用户表中添加一个字段并使用 ORDER BY。到目前为止,我想出了 2 个解决方案:
我的第一个解决方案是进行夜间批处理作业,计算每个可能的用户组合的评分并将其存储在单独的表中(user1、user2、rating)。然后我可以将此表与用户表连接起来,并按评级对结果进行排序。在做了一些数学运算后,我发现这个解决方案的扩展性不太好。
根据公式 n * (n - 1) / 2,10 个用户有 45 种可能的组合。对于 1.000 个用户,我突然不得不在我的评分表中插入 499.500 个评分组合。
第二个解决方案是保留 mysql 并在我的应用程序中即时计算评级。这也不能很好地扩展。假设搜索应该只向 UI 返回 100 个结果(最高评分在顶部)。如果我有 10.000 个用户,并且我想搜索居住在纽约的每个用户(按评分排序),我必须将居住在纽约的每个用户加载到我的应用程序中(比如说 3.000),应用算法然后只返回前 100 名给用户。通过这种方式,我从数据库中加载了 2.900 个无用的用户对象,并在算法上浪费了 CPU,而没有对其进行任何操作。
有什么想法可以在我的 MySQL 数据库或网络应用程序中进行设计,以便用户可以与其他每个用户进行单独评分,从而使系统扩展到几千个用户以上?
【问题讨论】:
我是n*(n-1)/2
,我不喜欢这个标题,但这个问题很有趣。
谢谢,我修正了公式。我对标题建议持开放态度.. 真的不知道如何表达它:-)
在第一步中,是否可以将最差的匹配项留在数据库中(例如,在 mysql 中可以很好扩展的更简单的算法),这样您只需要加载 - 比如说 500 个匹配项你的应用程序,这样你就可以提出一个不完整但几乎完美的结果?
【参考方案1】:
如果您必须将每个用户与其他每个用户进行匹配,那么无论您做什么,算法都是 O(N^2)。
如果您可以利用某种一维“指标”,那么您可以尝试将每个用户与单个合成值相关联。但这很尴尬,而且可能是不可能的。
但是您可以做的是注意哪些用户需要在他们的个人资料中进行更改(只要匹配所基于的任何参数发生更改)。此时,您可以只为这些用户批量重新计算表,因此在 O(N) 中工作:如果您有 10000 个用户并且只有 10 个需要重新计算,则您必须检查 100,000 条记录而不是 100,000,000 条。
其他策略是仅对比较有可能被比较的记录运行主要算法:在您的示例中,“同一城市”。或者在更新记录时(但这需要存储(user_1,user_2,ranking,last_calculated),只重新计算那些排名高、非常旧或从未计算过的记录。排名最低的匹配不太可能改变太多以至于它们浮动很快就登顶了。
更新
问题也在于 O(N^2) 存储空间。
如何减少这个空间?我想我可以看到两种方法。一种是不将一些信息放在匹配表中。 “匹配”功能越是刚性和陡峭越有意义;一万个“好的匹配”意味着匹配意味着很少。因此,当 User1 更改一些关键数据时,我们仍然需要重新计算大量数据,以防它将 User1 的一些“no-no”匹配带回“maybe”区域。但是我们会为每个用户保留一个较小的活跃匹配群。
存储仍会呈二次方增长,但不那么陡峭。
另一种策略是重新计算匹配,然后我们需要开发一些方法来快速选择哪些用户可能有良好的匹配(从而限制由JOIN),以及一些快速计算匹配的方法;这可能需要以某种方式将 User1 和 User2 之间的匹配重写为 DataUser1 子集 DataUser2 的一个非常简单的函数(可能使用辅助列)。
挑战在于利用 MySQL 的功能并将一些计算卸载到 MySQL 引擎。
为此,您可能会在输入时间(因此在 O(k) 中)将一些数据“映射”到空间信息或字符串并使用 Levenshtein 距离。
单个用户的存储会增长,但会线性增长,而不是二次增长,而且 MySQL SPATIAL
索引非常有效。
【讨论】:
我喜欢只为实际需要重新计算的用户重新计算评分的解决方案。但是对于系统中的 1000 个用户,我仍然需要在我的评级表中有 500000 个条目。一旦我达到 10000 个用户,评级表已经增长到 5000 万个条目。我从来没有在一个表中操作过这么多条目,所以我很好奇 MySQL 是否仍然能够在合理的时间内对这样的表进行连接? 你需要使用一些技巧而不是matches
table。我试图提出一些建议。【参考方案2】:
如果搜索应该只返回前 100 个最佳匹配项,那么为什么不只存储这些?听起来您无论如何都不想搜索结果的底端,所以不要计算它们。
那样,你的存储空间只有o(n),而不是o(n^2),更新也应该是。如果有人真的想查看前 100 个之后的匹配项(并且您想让它们),那么您可以选择在那时实时运行查询。
【讨论】:
如果您只想显示前 100 名而不显示其他内容(我也想过这样做),那么这很有效。只要您还允许用户按其他标准(年龄、城市、..)进行过滤,并且只按评级对结果进行排序,它就不再起作用了。【参考方案3】:我同意@Iserni 所说的一切。
如果您有一个网络应用程序并且用户需要“登录”,那么您可能有机会创建该用户当时的排名并将其存储到临时表(或现有表中的行)中。
如果计算所需的所有数据都适合内存,这将在合理的时间内(几秒钟)起作用。然后数据库引擎应该进行全表扫描并创建所有评级。
对于一个登录的用户来说,这应该工作得相当好。对于两个 . . .但是如果你有一打用户在一秒钟内登录,它就不会很好地扩展。
不过,从根本上说,您的评分并不能很好地衡量。您必须将所有用户与所有用户进行比较才能获得结果。无论是批处理(夜间)还是实时(当有人查询时)都不会改变问题的性质。会占用大量计算资源,多个用户同时发出请求会成为瓶颈。
【讨论】:
以上是关于用于 n * (n - 1) / 2 算法的 MySQL 架构的主要内容,如果未能解决你的问题,请参考以下文章
求n个数的全排列,n不定。用c语言。用于银行家算法中求安全序列