我手边有一个算法问题。为了便于解释这个问题,我将使用一个简单的类比。 我有一个输入文件
Country,Exports
Austrailia,Sheep
US, Apple
Austrialia,Beef
结束目标: 我必须找到这对国家之间的共同产品
{"Austrailia,New Zealand"}:{"apple","sheep}
{"Austrialia,US"}:{"apple"}
{"New Zealand","US"}:{"apple","milk"}
流程:
我读入输入并将其存储在TreeMap>在List中,字符串由于许多重复而被实现。 基本上,我按国家汇总。 其中Key是country,值是Exports。
{"austrailia":{"apple","sheep","koalas"}}
{"new zealand":{"apple","sheep","milk"}}
{"US":{"apple","beef","milk"}}
我有大约1200个密钥(国家),总值(出口)总共是8000万。 我对每个键的所有值进行排序:
{"austrailia":{"apple","sheep","koalas"}} -- > {"austrailia":{"apple","koalas","sheep"}}
这很快,因为只有1200个列表需要排序。
for(k1:keys)
for(k2:keys)
if(k1.compareTo(k2) <0){ //Dont want to double compare
List<String> intersectList = intersectList_func(k1's exports,k2's exports);
countriespair.put({k1,k2},intersectList)
}
这个代码块需要很长时间。我意识到它是O(n2)和大约1200 * 1200的比较。因此,运行了近3个小时到现在.. 有什么办法,我可以加快速度或优化它。 算法明智是最佳选择,还是需要考虑其他技术。
修改: 由于两个List都是事先排序的,所以intersectList是O(n),其中n是floor的长度(listOne.length,listTwo.length)和NOT O(n2),如下所述
private static List<String> intersectList(List<String> listOne,List<String> listTwo){
int i=0,j=0;
List<String> listResult = new LinkedList<String>();
while(i!=listOne.size() && j!=listTwo.size()){
int compareVal = listOne.get(i).compareTo(listTwo.get(j));
if(compareVal==0){
listResult.add(listOne.get(i));
i++;j++;} }
else if(compareVal < 0) i++;
else if (compareVal >0) j++;
}
return listResult;
}
11月22日更新 我目前的实施仍然运行了近18个小时。 :|
11月25日更新 我按照Vikram和其他几个人的建议运行了新的实现。这个星期五一直在运行。 我的问题是,如何通过出口而不是国家进行分组可以节省计算复杂性。我发现复杂性是一样的。正如Groo所说,我发现第二部分的复杂性是O(E * C ^ 2),其中E是出口,C是国家。
答案 0 :(得分:2)
存储类似于以下数据结构的内容: - (以下是伪代码)
ValuesSet ={
apple = {"Austrailia","New Zealand"..}
sheep = {"Austrailia","New Zealand"..}
}
for k in ValuesSet
for k1 in k.values()
for k2 in k.values()
if(k1<k2)
Set(k1,k2).add(k)
时间复杂:O(没有与同类产品不同的对)
注意:我可能错了,但我不认为你可以减少这个时间的复杂性
以下是针对您的问题的java实现: -
public class PairMatching {
HashMap Country;
ArrayList CountNames;
HashMap ProdtoIndex;
ArrayList ProdtoCount;
ArrayList ProdNames;
ArrayList[][] Pairs;
int products=0;
int countries=0;
public void readfile(String filename) {
try {
BufferedReader br = new BufferedReader(new FileReader(new File(filename)));
String line;
CountNames = new ArrayList();
Country = new HashMap<String,Integer>();
ProdtoIndex = new HashMap<String,Integer>();
ProdtoCount = new ArrayList<ArrayList>();
ProdNames = new ArrayList();
products = countries = 0;
while((line=br.readLine())!=null) {
String[] s = line.split(",");
s[0] = s[0].trim();
s[1] = s[1].trim();
int k;
if(!Country.containsKey(s[0])) {
CountNames.add(s[0]);
Country.put(s[0],countries);
k = countries;
countries++;
}
else {
k =(Integer) Country.get(s[0]);
}
if(!ProdtoIndex.containsKey(s[1])) {
ProdNames.add(s[1]);
ArrayList n = new ArrayList();
ProdtoIndex.put(s[1],products);
n.add(k);
ProdtoCount.add(n);
products++;
}
else {
int ind =(Integer)ProdtoIndex.get(s[1]);
ArrayList c =(ArrayList) ProdtoCount.get(ind);
c.add(k);
}
}
System.out.println(CountNames);
System.out.println(ProdtoCount);
System.out.println(ProdNames);
} catch (FileNotFoundException ex) {
Logger.getLogger(PairMatching.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(PairMatching.class.getName()).log(Level.SEVERE, null, ex);
}
}
void FindPairs() {
Pairs = new ArrayList[countries][countries];
for(int i=0;i<ProdNames.size();i++) {
ArrayList curr = (ArrayList)ProdtoCount.get(i);
for(int j=0;j<curr.size();j++) {
for(int k=j+1;k<curr.size();k++) {
int u =(Integer)curr.get(j);
int v = (Integer)curr.get(k);
//System.out.println(u+","+v);
if(Pairs[u][v]==null) {
if(Pairs[v][u]!=null)
Pairs[v][u].add(i);
else {
Pairs[u][v] = new ArrayList();
Pairs[u][v].add(i);
}
}
else Pairs[u][v].add(i);
}
}
}
for(int i=0;i<countries;i++) {
for(int j=0;j<countries;j++) {
if(Pairs[i][j]==null)
continue;
ArrayList a = Pairs[i][j];
System.out.print("\n{"+CountNames.get(i)+","+CountNames.get(j)+"} : ");
for(int k=0;k<a.size();k++) {
System.out.print(ProdNames.get((Integer)a.get(k))+" ");
}
}
}
}
public static void main(String[] args) {
PairMatching pm = new PairMatching();
pm.readfile("Input data/BigData.txt");
pm.FindPairs();
}
}
答案 1 :(得分:2)
这可以在一个语句中使用SQL作为自联接来完成:
测试数据。首先创建一个测试数据集:
Lines <- "Country,Exports
Austrailia,Sheep
Austrailia,Apple
New Zealand,Apple
New Zealand,Sheep
New Zealand,Milk
US,Apple
US,Milk
"
DF <- read.csv(text = Lines, as.is = TRUE)
sqldf 现在我们已DF
发出此命令:
library(sqldf)
sqldf("select a.Country, b.Country, group_concat(Exports) Exports
from DF a, DF b using (Exports)
where a.Country < b.Country
group by a.Country, b.Country
")
给出这个输出:
Country Country Exports
1 Austrailia New Zealand Sheep,Apple
2 Austrailia US Apple
3 New Zealand US Apple,Milk
带索引的 如果速度太慢,请在“国家/地区”列中添加索引(请务必不要忘记main.
部分:
sqldf(c("create index idx on DF(Country)",
"select a.Country, b.Country, group_concat(Exports) Exports
from main.DF a, main.DF b using (Exports)
where a.Country < b.Country
group by a.Country, b.Country
"))
如果你耗尽内存,那么添加dbname = tempfile()
sqldf参数,使其使用磁盘。
答案 2 :(得分:1)
[更新]与OP的原始算法相比,此处提供的算法不应该提高时间复杂度。两种算法都具有相同的渐近复杂度,并且通过排序列表进行迭代(如OP所做的)通常应该比使用哈希表更好。
您需要按product
而不是country
对项目进行分组,以便能够快速获取属于某个产品的所有国家/地区。
这将是伪代码:
inputList contains a list of pairs {country, product}
// group by product
prepare mapA (product) => (list_of_countries)
for each {country, product} in inputList
{
if mapA does not contain (product)
create a new empty (list_of_countries)
and add it to mapA with (product) as key
add this (country) to the (list_of_countries)
}
// now group by country_pair
prepare mapB (country_pair) => (list_of_products)
for each {product, list_of_countries} in mapA
{
for each pair {countryA, countryB} in list_of_countries
{
if mapB does not countain country_pair {countryA, countryB}
create a new empty (list_of_products)
and add it to mapB with country_pair {countryA, countryB} as key
add this (product) to the (list_of_products)
}
}
如果您的输入列表长度为N,并且您有不同的C国家和P个不同的产品,则此算法的运行时间应为O(N)
第一部分,O(P*C^2)
为第二部分。由于您的最终列表需要国家/地区映射到产品列表,因此我认为您不会失去P*C^2
复杂性情况下。
我没有用Java编写太多代码,所以我添加了一个C#示例,我相信你可以很容易地移植它:
// mapA maps each product to a list of countries
var mapA = new Dictionary<string, List<string>>();
foreach (var t in inputList)
{
List<string> countries = null;
if (!mapA.TryGetValue(t.Product, out countries))
{
countries = new List<string>();
mapA[t.Product] = countries;
}
countries.Add(t.Country);
}
// note (this is very important):
// CountryPair tuple must have value-type comparison semantics,
// i.e. you need to ensure that two CountryPairs are compared
// by value to allow hashing (mapping) to work correctly, in O(1).
// In C# you can also simply use a Tuple<string,string> to
// represent a pair of countries (which implements this correctly),
// but I used a custom class to emphasize the algorithm
// mapB maps each CountryPair to a list of products
var mapB = new Dictionary<CountryPair, List<string>>();
foreach (var kvp in mapA)
{
var product = kvp.Key;
var countries = kvp.Value;
for (int i = 0; i < countries.Count; i++)
{
for (int j = i + 1; j < countries.Count; j++)
{
var pair = CountryPair.Create(countries[i], countries[j]);
List<string> productsForCountryPair = null;
if (!mapB.TryGetValue(pair, out productsForCountryPair))
{
productsForCountryPair = new List<string>();
mapB[pair] = productsForCountryPair;
}
productsForCountryPair.Add(product);
}*
}
}
答案 3 :(得分:0)
这是使用Map Reduce的一个很好的例子。
您将受益于可以分布到群集中的分布式并行算法。
答案 4 :(得分:0)
你实际上正在服用O(1 ^相交需要n ^ 2 *。
让我们看看我们是否可以改善交叉时间。我们可以为每个存储相应产品的国家/地区维护地图,因此您有n个国家/地区的n个哈希映射。只需要通过所有产品迭代一次进行初始化。如果要快速查找,请将地图地图维护为:
HashMap<String,HashMap<String,Boolean>> countryMap = new HashMap<String, HashMap<String,Boolean>>();
现在,如果您想查找国家/地区str1和str2的常见产品,请执行以下操作:
HashMap<String,Boolean> map1 = countryMap.get("str1");
HashMap<String,Boolean> map2 = countryMap.get("str2");
ArrayList<String > common = new ArrayList<String>();
Iterator it = map1.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Boolean> pairs = (Map.Entry)it.next();
//Add to common if it is there in other map
if(map2.containsKey(pairs.getKey()))
common.add(pairs.getKey());
}
所以,如果在一个地图中有k个条目,假设哈希映射查找实现是O(1)(我猜它是java的log k),那么它总是O(n ^ 2 * k)。
答案 5 :(得分:0)
在必要时使用散列图加快速度:
1)浏览数据并使用键创建地图项目和值与该项目相关联的国家/地区列表。所以例如绵羊:澳大利亚,美国,英国,新西兰....
2)创建一个带有每对国家/地区的键的哈希映射,并且(最初)将一个空列表作为值。
3)对于每个项目,检索与其关联的国家/地区列表以及该列表中的每对国家/地区,将该项目添加到步骤(2)中为该对创建的列表中。
4)现在输出每对国家的更新列表。
最大的成本在步骤(3)和(4)中,并且这两种成本在产生的产出量上是线性的,所以我认为这与最优成本相差不远。