我正在尝试编写一个JS函数,它有两个参数,include和exclude,每个参数都是一个对象{X,Y}的数组,表示从X到Y的数字范围,两者都包括在内。
输出是减去include中所有范围的所有范围。
例如:
include = [ {1,7}, {9,10}, {12,14} ]
exclude = [ {4,5}, {11,20} ]
output = [ {1,3}, {6,7}, {9,10} ]
答案 0 :(得分:1)
您可以使用扫描线算法。对于每个数字保存它代表的内容(开始和结束,包含和排除)。然后将所有数字放在一个数组中并对其进行排序。然后迭代地从数组中删除元素并执行适当的操作。
include_list = [[1,7]]
exclude_list = [[4,5]]
(1,start,inclusion),(4,start,exclusion),(5,end,exclusion),(7,end,inclusion)
include = 0
exclude = 0
cur_element = (1,start,inclusion) -> include = 1, has_open_range = 1, range_start = 1 // we start a new range starting at 1
cur_element = (4,start,exclusion) -> exclude = 1, has_open_range = 0, result.append ( [1,4] ) // we close the open range and add range to result
cur_element = (5,end,exclusion) -> exclude = 0, has_open_range = 1, range_start = 5 // because include was 1 and exclude become 0 we must create a new range starting at 5
cur_element = (7,end,inclusion) -> include = 0, has_open_range = 0, result.append([5,7]) // include became zero so we must close the current open range so we add [5,7] to result
维护变量include
和exclude
使用相应元素的开头递增它们,并在接收到结束元素时递减它们。根据{{1}}和include
的值,你可以确定你应该开始一个新范围,关闭开放范围,或者什么也不做。
该算法以线性时间O(n)运行。
答案 1 :(得分:0)
注意:include = [ {1,7}, {9,10}, {12,14} ]
不是有效的javascript,所以我假设你传递的是数组数组,例如:
include = [ [1,7], [9,10], [12,14] ]
蛮力方法(解决方案,可能不是最有说服力的):
function solve_range(include, exclude) {
numbers = [];
include.forEach(function (range) {
for (i = range[0]; i <= range[1]; i++) {
numbers[i] = true;
}
});
exclude.forEach(function (range) {
for (i = range[0]; i <= range[1]; i++) {
numbers[i] = false;
}
});
contiguous_start = null;
results = [];
for (i = 0; i < numbers.length; i++) {
if (numbers[i] === true) {
if (contiguous_start == null) {
contiguous_start = i;
}
} else {
if (contiguous_start !== null) {
results[results.length] = [contiguous_start, i - 1];
}
contiguous_start = null;
}
}
return results;
}
var include = [
[1, 7],
[9, 10],
[12, 14]
];
var exclude = [
[4, 5],
[11, 20]
];
var output = solve_range(include, exclude);
答案 2 :(得分:0)
用于减去两组X,Y
的整数集算术的规则是
X − Y := {x − y | x ∈ X, y ∈ Y }
但这似乎不是你想要的。
您可以在示例中假设有序集,它允许您将每个出现的x==y
设置为JavaScript数组中的任意值,并使用它来分割。但你不需要那样。
设置差异{1...7}\{4...5}
扩展为{1,2,3,4,5,6,7}\{4,5}
。您可以很容易地看到,使用设置算术规则的减法将保留{1,2,3,0,0,6,7}
,并使用正常的设置减法(符号\
)得到{1,2,3,6,7}
。
设定差异{12...14}\{11...20}
扩展为{12,13,14}\{11,12,13,14,15,16,17,18,19,20}
;设定算术。差异是{-11,0,0,0,-15,-16,..., - 20},但正常的set-subtraction留下空集{}
。
使用空集处理操作等同于算术集规则的常规算术{x}-{}={x}
和{}-{x} = {-x}
以及具有正常规则的{x}\{}={x}
,{}\{x}= {}
因此,根据您的示例,您必须使用的是正常的设置规则。没有必要扩展集合,可以假设它们是密集的。
您可以使用相对差异(您可以称之为距离)。
使用{1...7}\{4...5}
时,第一个开头很小,然后第二个开始,第一个结尾比第二个结尾更大,这导致了两个不同的集合。
使用{12...14}\{11...20}
时,第一个开头大于第二个开始,第一个结尾低于第二个结尾,导致空集。
第三个例子使用了空集规则。
您需要一个示例代码段吗?
答案 3 :(得分:0)
这里的答案适用于分数而且不仅仅是暴力强迫。我已经添加了评论来解释它是如何工作的。这可能看起来很大,前提很简单:
创建一个接受点p1_excluding_p2
和p1
的方法p2
,并返回执行p1 - p2
后存在的点数组
创建一个方法points_excluding_p2
,它执行与上面相同的操作,但这次允许我们传递一个points
数组,并返回减去{后存在的点数组{1}}来自我们数组中的所有点,现在我们有p2
创建一个方法(points) - p2
,它采用与上面相反的输入。这一次,接受一个点p1_excluding_all
和许多排除点,并在减去所有排除点后返回剩余的点数组。这实际上很容易创建。我们只需从p1
和第一个排除点([p1]
)开始,然后将其反馈到exclusion1
。我们使用points_excluding_p2
获取返回的数组(将p1 - exclusion1
)并将此提供给points_excluding_p2
。我们会继续这一过程,直到我们排除每个排除点,然后我们留下一系列exclusion2
现在我们有能力执行p1 - (all exclusion points)
,只需循环遍历所有点并致电p1 - (all exclusion points)
,我们就会留下一个数组点减去每个排除点。我们通过p1_excluding_all
运行我们的结果,如果我们有任何重复的条目,那就是它。
代码:
remove_duplicates
返回:
var include = [ [1,7], [9,10], [12,14] ]
var exclude = [ [4,5], [11,20] ]
/* This method is just a small helper method that takes an array
* and returns a new array with duplicates removed
*/
function remove_duplicates(arr) {
var lookup = {};
var results = [];
for(var i = 0; i < arr.length; i++) {
var el = arr[i];
var key = el.toString();
if(lookup[key]) continue;
lookup[key] = 1;
results.push(el);
}
return results;
}
/* This method takes 2 points p1 and p2 and returns an array of
* points with the range of p2 removed, i.e. p1 = [1,7]
* p2 = [4,5] returned = [[1,3],[6,7]]
*/
function p1_excluding_p2(p1, p2) {
if(p1[1] < p2[0]) return [p1]; // line p1 finishes before the exclusion line p2
if(p1[0] > p2[1]) return [p1]; // line p1 starts after exclusion line p1
var lines = [];
// calculate p1 before p2 starts
var line1 = [ p1[0], Math.min(p1[1], p2[0]-1) ];
if(line1[0] < line1[1]) lines.push(line1);
// calculate p1 after p2 ends
var line2 = [ p2[1]+1, p1[1] ];
if(line2[0] < line2[1]) lines.push(line2);
// these contain the lines we calculated above
return lines;
}
/* this performs the exact same operation as above, only it allows you to pass
* multiple points (but still just 1 exclusion point) and returns results
* in an identical format as above, i.e. points = [[1,7],[0,1]]
* p2 = [4,5] returned = [[0,1],[1,3],[6,7]]
*/
function points_excluding_p2(points, p2) {
var results = [];
for(var i = 0; i < points.length; i++) {
var lines = p1_excluding_p2(points[i], p2);
results.push.apply(results, lines); // append the array lines to the array results
}
return results;
}
/* this method performs the same operation only this time it takes one point
* and multiple exclusion points and returns an array of the results.
* this is the important method of: given 1 point and many
* exclusion points, return the remaining new ranges
*/
function p1_excluding_all(p1, excluded_pts) {
var checking = [p1];
var points_leftover = [];
for(var i = 0; i < exclude.length; i++) {
checking = points_excluding_p2(checking, exclude[i]);
}
return remove_duplicates(checking);
}
/* now that we have a method that we can feed a point and an array of exclusion
* points, its just a simple matter of throwing all our points into this
* method, then at the end remove duplicate results for good measure
*/
var results = [];
for(var i = 0; i < include.length; i++) {
var lines = p1_excluding_all(include[i], exclude);
results.push.apply(results, lines); // append the array lines to the array results
}
results = remove_duplicates(results);
console.log(results);
答案 4 :(得分:0)
这是一个工作解决方案,可处理排除范围的4种可能的重叠方案。
var include = [{from:1, to: 7},{from: 9, to: 10},{from: 12, to: 14}];
var exclude = [{from:4, to: 5}, {from: 11, to: 20}];
//result: {1,3}, {6,7}, {9,10}
var resultList = [];
for (var i=0;i<include.length;i++){
var inc = include[i];
var overlap = false;
for (var x=0;x<exclude.length;x++ ){
var exc = exclude[x];
//4 scenarios to handle
if (exc.from >= inc.from && exc.to <= inc.to){
//include swallows exclude - break in two
resultList.push({from: inc.from, to: exc.from - 1});
resultList.push({from: exc.to + 1, to: inc.to});
overlap = true;
}else if (exc.from <= inc.from && exc.to >= inc.to){
//exclude swallows include - exclude entire range
overlap = true;
break;
}else if (exc.from <= inc.from && exc.to <= inc.to && exc.to >= inc.from){
//exclusion overlaps on left
resultList.push({from: exc.to, to: inc.to});
overlap = true;
}else if (exc.from >= inc.from && exc.to >= inc.to && exc.from <= inc.to){
//exclusion overlaps on right
resultList.push({from: inc.from, to: exc.from - 1});
overlap = true;
}
}
if (!overlap){
//no exclusion ranges touch the inclusion range
resultList.push(inc);
}
}
console.log(resultList);
答案 5 :(得分:0)
也许我们可以通过将标记的区间合并到一个排序列表中来提高效率:
include = [ {1,7}, {9,10}, {12,14} ]
exclude = [ {4,5}, {11,20} ]
merged = [ [1,7,0], [4,5,1], [9,10,0], [11,20,1], [12,14,0] ];
然后,遍历列表,对于任何排除的间隔,更新任何周围受影响的间隔。