关于复杂性和交集的复杂挑战

时间:2017-01-30 11:37:09

标签: javascript math time-complexity computer-science complexity-theory

前言

注意:这个问题与复杂性有关。我在这里使用了一个复杂的设计模式,为了理解这个问题你不需要理解。我可以更简化它,但为了防止错误,我选择保持它相对不受影响。代码是用TypeScript编写的,TypeScript是一套超级JavaScript。

代码

请注意以下课程:

export class ConcreteFilter implements Filter {
    interpret() {
        // rows is a very large array
        return (rows: ReportRow[], filterColumn: string) => {
            return rows.filter(row => {
                // I've hidden the implementation for simplicity, 
                // but it usually returns either an empty array or a very short one.
                }
            }).map(row => <string>row[filterColumn]);
        }
    }
}

它接收一个报告行数组,然后它通过我隐藏的一些逻辑过滤数组。最后,它不返回整行,而只返回filterColumn中提到的一个字符串列。

现在,看一下以下函数:

function interpretAnd (filters: Filter[]) {
    return (rows: ReportRow[], filterColumn: string) => {
        var runFilter = filters[0].interpret();
        var intersectionResults = runFilter(rows, filterColumn);

        for (var i=1; i<filters.length; i++) {
            runFilter = filters[i].interpret();
            var results = runFilter(rows, filterColumn);
            intersectionResults = _.intersection(intersectionResults, results);
        }

        return intersectionResults;
    }
}

它接收一个过滤器数组,并返回过滤器返回的所有“filterColumn”的不同数组。

在for循环中,我从每个过滤器获取结果(字符串数组),然后进行交叉操作。

问题

报告行数组很大,因此每个runFilter操作都很昂贵(而另一方面,过滤器数组非常短)。我希望尽可能少地迭代报表行数组。此外,runFilter操作很可能返回零结果或非常少。

解释

假设我有3个过滤器和10亿个报告行。内部iterration,即ConcreteFilter中的迭代,将发生30亿次,即使第一次执行runFilter返回0结果,所以我有20亿次冗余迭代。

因此,我可以在每次迭代开始时检查intersectionResults是否为空,如果是,则打破循环。但我确信在数学上有更好的解决方案。

此外,如果返回的第一个runFIlter exectuion说15个结果,我希望下一个exectuion只接收15个报告行的数组,这意味着我希望交集操作影响下一次调用{}的输入{1}}。

我可以在每次迭代后修改报表行数组,但是我没有看到如何以一种比现在更贵的方式来实现它。

一个好的解决方案是删除runFilter操作,然后在每个操作中传递已经过滤的数组而不是整个数组,但是我不允许这样做,因为我不能改变结果过滤器接口的格式。

我的问题

我想获得您能想到的最佳解决方案以及解释。

非常感谢每一个愿意花时间帮助我的人。

1 个答案:

答案 0 :(得分:1)

不确定这会有多有效,但这是您可以采取的一种可行方法。如果您通过过滤器列预处理行,则可以检索匹配的行。如果您通常拥有2个以上的过滤器,那么这种方法可能会更有益,但是它会占用大量内存。您可以根据过滤器的数量来分支方法。可能有一些TS构造更有用,不太熟悉它。下面的代码中有一些注释:

var map = {};

// Loop over every row, keep a map of rows with a particular filter value.
allRows.forEach(row => {
    const v = row[filterColumn];
    let items;
    items = map[v] = map[v] || [];
    items.push(row)
});

let rows = allRows;
filters.forEach(f => {
    // Run the filter and return the unique set of matched strings
    const matches = unique(f.execute(rows, filterColumn));
    // For each of the matched strings, go and look up the remaining rows and concat them for the next filter.
    rows = [].concat(...matches.reduce(m => map[v]));
});

// Loop over the rows that made it all the way through, extract the value and then unique() the collection
return unique(rows.map(row => row[filterColumn]));

再考虑一下,您可以使用类似的方法,但只是在每个过滤器的基础上进行:

let rows = allRows;
filters.forEach(f => {
    const matches = f.execute(rows, filterColumn);
    let map = {};
    matches.forEach(m => {
        map[m] = true;
    });

    rows = rows.filter(row => !!map[row[filterColumn]]);
});
return distinctify(rows.map(row => row[filterColumn]));