bounty将在2天后过期。回答此问题可获得+400声望奖励。Samuel Squire正在查找规范答案:我正在寻找一个解决方案,因为它可以用作编程语言的原语
如果我有很多字符串集合,每个字符串都有不同的元素,其中一些元素是重叠的。
我要搜索的集合是搜索集合中所有项目的并集。
我能用散列法做点什么吗?
联合查找算法是否正确?
我想要的是布隆过滤器吗?
在下面的示例中,我可以首先依靠HashSet的equals
来检查是否存在精确匹配。
我搜索每个集合,并执行containsAll
以查看它是否与搜索集合并。
举个例子,如果我有“水果”集[“橙子”,“猕猴桃”,“苹果”]和“蔬菜”集[“卷心菜”,“胡萝卜”,“花椰菜”].我想搜索[“橘子”,“猕猴桃”]来匹配“水果”
一定有比这更好的办法,但我不确定。
package main;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class SetKeyHash {
public static void main(String args[]) {
HashSet<String> vegetables = new HashSet<>();
vegetables.add("tomato");
vegetables.add("carrot");
vegetables.add("broccoli");
HashSet<String> fruit = new HashSet<>();
fruit.add("apple");
fruit.add("kiwi");
fruit.add("orange");
Map<HashSet<String>, String> map = new HashMap<>();
map.put(vegetables, "vegetables");
map.put(fruit, "fruit");
HashSet<String> search = new HashSet<>();
search.add("tomato");
search.add("carrot");
search.add("broccoli");
System.out.println("Exact set search");
System.out.println(map.get(search));
System.out.println("Partial set search");
HashSet<String> partialSearch = new HashSet<>();
partialSearch.add("tomato");
partialSearch.add("broccoli");
for (HashSet<String> set : map.keySet()) {
if (set.containsAll(partialSearch)) {
System.out.println(String.format("Found partial match %s", map.get(set)));
}
}
}
}
5条答案
按热度按时间k2fxgqgv1#
1.您需要改用
Map<String, HashSet<String>> map = new HashMap<>();
。1.以下解决方案查找包含“search”集合中所有元素的所有集合名称。
8mmmxcuj2#
据我所知,你的问题是要把不同的蔬菜和水果分类,并把蔬菜和水果的名称只储存一次,而且你要根据食物的名称来检索食物的种类。
你描述搜索查询的方式,
“如果我将“水果”设置为[“橙子”、“猕猴桃”、“苹果”],将“蔬菜”设置为[“卷心菜”、“胡萝卜”、“花椰菜”]。我希望搜索[“橘子”、“猕猴桃”]以匹配“水果”。“
你必须遍历参数数组([“橙子”,“kiwi”])并寻找匹配项。你可以计算搜索参数中有多少是蔬菜,有多少是水果。
存储数据的工作方式与Moemen描述的方式相同。
Map<String, HashSet<String>> map = new HashMap<>();
变量的类型(“vegeticles”,“fruits”)将是存储为字符串的键,并且您的项将位于哈希集中。
整个Map看起来如下所示:
Map-〉“蔬菜”:[“番茄”、“花椰菜”、“胡萝卜”],
第二种方法是有一个字符串Map指向字符串。
Map=〉{字符串:字符串}
您可以输入数据并将其设置为:
Map-〉“橙子”:“水果”,
这样,您就可以在搜索食物项中运行一个循环,并检索它们是水果还是蔬菜。
我几乎可以肯定,食物组合不能用来查询Map。这意味着,你必须一个元素一个元素地去。所以[“橙子”,“apple”],你必须有一个循环来查找orange的类型,然后是apple。
希望这能回答您的问题,并让您对规划数据存储结构有一个深入的了解。
ddarikpa3#
另一种方法是在组件集上准备一个索引,这里的组件集是
fruits
和vegetables
一个Python的方法是这样做的,我相信类似的可以很容易地在java中完成
其中,查找只是元素的反向命令
如果您想为使用SQL和表的服务解决这个问题,我建议查看Postgres中的GIN索引。
编辑(添加)
intersection
的一个替代方案是如下的计数器,不一定是优化(集合交集只会查看最小的集合)ia2d9nvy4#
实际上你的问题很宽泛,这个主题很大,有很多细节被省略了,但是,我会提供和解释不同的方法,我们也会尝试回顾你提到的布隆过滤器和并集运算。
让我们先考虑一些假设
为了简化代码编写,我们将使用Java Google Guava library
要添加到项目中的Maven
pom.xml
依赖项:话虽如此,让我们看看不同的方法:
∮相当直接的方式∮
当我们处理字符串时,如果字符串很长,可能会占用太多的内存空间,但只要我们不关心这些字符串在集合中的实际值,我们可以只使用它们的哈希码,如果字符串很短,那么就不需要哈希,我们可以直接使用它们.
假设我们的字符串可能很长(在真实的世界中,例如URL)。我们应该使用什么哈希算法呢?嗯,这确实取决于,但主要目标是实现唯一性,以尽可能防止冲突。因此,在本例中,我们将使用SHA-256哈希算法,它提供256位输出。
因此,在这种方法中,我们搜索交集的方式如下:
注意:在遍历集合时,我们首先跳过所有小于搜索集的集合,这只是为了优化。显然,检查这些集合是没有意义的,因为它保证了它们没有足够的元素与我们的搜索集完全相交。此外,如果你想检查搜索集是否是目标集的实际示例,你可以添加
equals()
检查。使用布隆过滤器
Bloom filter是一种概率数据结构,它很适合检查元素是否肯定不在集合中,但不能肯定地告诉你某个东西是否在集合中。查询返回“可能在集合中”或“肯定不在集合中”。布隆过滤器不存储元素本身,这是关键的一点,你不用布隆过滤器来测试一个元素是否存在,你用它来测试它是否确实不存在,因为它保证没有假否定。并且与散列表/散列集等相比,它占用显著更少的空间,并且加速检查。
布隆过滤器的工作原理是:获取元素,将它们通过N个不同的哈希函数,每个函数都有一个伴随的位向量,然后根据观察到的哈希值将伴随向量中的位置设置为True。一旦位置被翻转为True(1),它就永远不会被设置回False(0)。
不同散列函数的数量N以及位向量的长度通常在实现中基于配置的容量和最大允许错误率进行逆向工程。容量是我们期望布隆过滤器看到的不同元素的数量。
当然,它也有缺点:
因此,使用Guava库实现了布隆过滤器,我们可以写我们的代码如下:
注意
expectedAssertions
参数,这是fpp
--期望的误报概率(必须为正且小于1.0)。我们提供的值越小,我们的bloom就越准确,然而,它越准确,我们的bloom占用的内存就越多。这里我们在遍历我们的blooms,用
mightContain(value)
检查我们的搜索集合中的每个元素,如果它是false
,那么我们确定不是这样的,这个bloom和我们的集合不相交。布隆过滤器上的交集和并集运算
在前面的方法中,我们使用布隆过滤器检查搜索集合中的每个元素。您可以对两个布隆过滤器执行的一个操作是合并它们,这意味着如果您有布隆过滤器A和布隆过滤器B,最后得到包含来自A和B的所有唯一项的第三布隆过滤器C。联合两个布隆过滤器的方法是,对A和B中的每一位进行位或运算,得到C的位数。计算简单快速。
但是这个操作本身在我们的例子中并没有什么用处。但是,我们可以使用它来执行交集检查。我们如何实现这一点呢?
好吧,这里面包含了一些数学:
计数(交集(A,B))=(计数(A)+计数(B))-计数(并集(A,B))
话虽如此,让我们尝试执行此操作。
将之前方法的代码段更改为以下代码:
和
intersectWithUnion()
方法:在这里,我们首先从搜索集中创建一个bloom。然后用我们的新方法检查每个bloom的交集。联合操作是在Guava库中用putAll()方法实现的。我们为什么要在搜索bloom上调用
copy()
呢?正如putAll()
的文档所述:通过对基础数据执行按位OR,将此Bloom筛选器与另一个Bloom筛选器组合在一起。变异发生在此示例上。调用方必须确保Bloom筛选器大小合适,以避免饱和。
所以,
putAll()
改变了我们的bloom,copy()
是我们的开销。重要提示:要执行联合操作,bloom必须兼容。基本上,它们必须具有相同的 expectedAssertions 和 fpp。要兼容,有以下要求:
否则将引发
IllegalArgumentException
。我们应该使用什么方法?
嗯,这个问题没有直接的答案,因为它确实取决于你实际情况中的许多因素。没有“总是更好”或“总是性能好”的情况。例如,当处理巨大数据量时,布隆过滤器非常有用,而且非常节省内存。数以百万计的字符串。但它有它自己的缺点。在简单的情况下,你没有这么多的数据,肯定是更好地使用一些直接的方法。希望它能帮助:)
此外,interesting article about bloom filters
4sup72z85#
你可以使用倒排索引,lucene使用它。
其他答案可能也是这样说的,把Map倒过来,一个一个地搜索项目。
如果你有很多这样的设置,最好插入到数据库中,比如ElasticSearch也使用倒排索引,或者使用mysql上的全文搜索引擎(仍然使用倒排索引)。