合并“空间”元组列表

时间:2010-02-25 07:39:42

标签: algorithm f#

我有三个元组列表,每个元组包含(startpos,endpos,value)。

我想要做的是将它们合并到一个元组列表中(startpos,endpos,values []),但遵循一条规则,我发现绘制比写入更容易:

//third               [---------]                    [------------]
//second        [-------------]              [---------------------------]
//first   [-----------------------------]    [--------------]
//(pos)   0123456789|123456789|123456789|123456789|123456789|123456789|123456789
//result  [--1-][--2-][---3---][---1----]    [---2--][---3--]

(结果中的数字表示每个结果元素的值[]列表的预期长度)

基本上,我只保留一个'更高'元素,它与'lower'元素重叠,然后我分成'同质'元素。

可以将这些职位视为int类型。从结果中可以看出,“拆分”段不会在同一位置开始和结束,而是在pos-1或pos + 1处。只要定义了值,这些值的顺序就不重要了。

示例数据(基于上面的示例):

let third =  [(12,22,3.1);(43,56,3.2)]
let second = [(6,20,2.1);(35,63,2.2)]
let first =  [(0,30,1.1);(35,50,1.2)]
let after =  [
                (0,5,[1.1]);
                (6,11,[1.1;2.1]);
                (12,20,[1.1;2.1;3.1]);
                (21,30,[1.1]);
                (35,42,[1.2;2.2]);
                (43,50,[1.2;2.2;3.2])
             ]

现在我发现很难以功能性的方式思考这个问题,任何想到的事情都是必要的。也许在这种情况下这是不可避免的,但如果有人有任何想法......

UPDATE 实际上,如果我们将输入案例概括为已经是类型(int*int*List<float>),我们可以只处理两个输入列表的情况,然后折叠它。

PS:这不是家庭作业,也不是代码高尔夫,我只是稍微消毒了数据。

2 个答案:

答案 0 :(得分:1)

您的after数据错误;至少我的程序认为它是,我相信它。 :)

let third =  [(12,22,3.1);(43,56,3.2)] 
let second = [(6,20,2.1);(35,63,2.2)] 
let first =  [(0,30,1.1);(35,50,1.2)] 

let all = List.concat [first; second; third]
let min = all |> Seq.map (fun (x,y,z)->x) |> Seq.min 
let max = all |> Seq.map (fun (x,y,z)->y) |> Seq.max

let setsEachValueIsIn =
    [min..max] 
    |> List.map (fun i -> 
        i, all 
            |> List.filter (fun (x,y,z) -> x<=i && i<=y) 
            |> List.map (fun (x,y,z) -> z))

printfn "%A" setsEachValueIsIn

let x1,l1 = Seq.nth 0 setsEachValueIsIn

let result = 
    setsEachValueIsIn 
    |> List.fold (fun (((l,h,s)::t) as prev) (nx,ns) -> 
                    if s=ns then (l,nx,s)::t else (nx,nx,ns)::prev
                ) [x1,x1,l1]
    |> List.rev

let after =  [ 
                (0,5,[1.1]); 
                (6,11,[1.1;2.1]); 
                (12,20,[1.1;2.1;3.1]); 
                (21,30,[1.1]); 
                (35,42,[1.2;2.2]); 
                (43,50,[1.2;2.2;3.2]) 
             ] 

printfn ""
printfn "%A" result
printfn ""
printfn "%A" after
assert(result = after)

策略:首先,我将整个范围内的每个数字映射到“它所在的位置”。然后我折叠,播种第一个结果(min,min,setsMinIsIn)和每一步,如果集合没有改变,我只是扩大范围,否则如果集合确实改变,我会创建一个新元素。

折叠中var名称的键:l ow,h igh,s et,nx - next x,ns - next set < / p>

答案 1 :(得分:0)

完全重写(参见编辑),更短,更优雅,可能不太可读。仍然捏着Brian的逻辑。

更新:现在可以使用,至少适用于上面的测试

let third =  [(12,22,3.1);(43,56,3.2)]
let second = [(6,20,2.1);(35,63,2.2)]
let first =  [(0,30,1.1);(35,50,1.2)]

//===helper functions===
// foldable combined min and max finder
let minmax (mn,mx) (x,y,_) = (min mn x, max mx y)  
// test if x - y range overlaps position i
let overlaps i (x,y,_) = x<=i && i<=y 
// get third element from triple
let getz (_,_,z) = z              

//specialise function, given two tuples, will combine lists (L & U)
//  but only if both lists have contents AND their indexes (il & iu) 
//  are not more than 1 apart, i is included simply so that we can pass 
//  merge directly to the List.map2 below
let merge (i,il,L) (_,iu,U) = 
     if L = [] || U = [] || iu - il > 1 then 
        (i, il, L) 
     else  
        (i, iu, L @ U)

let input = [first;second;third] // input data - 'bottom' first
//find max and min positions
let (x0,yn) = input |> Seq.concat |> Seq.fold minmax (0,0) 

//transform each data list to a list of (i,[z])
let valsByPos = input |> List.map (fun level -> //for each data 'level' 
                [x0..yn] |> List.map (fun i ->   //for each position in range
                     //collect values of all elements in level that
                     // overlap this position
                     (i, level |> List.filter (overlaps i) |> List.map getz)))

// 'merge up' each level, keeping only upper values if lower values exist
// after we will have just one list of (i, [z])
let mergedValsByPos = valsByPos //offside here for SO formatting
          //add an index into each tuple
          |> List.mapi (fun i l -> l |> List.map (fun (j,z) -> (j,i,z)))  
          //use index to determine if we should 'merge up' for each subsublst
          |> List.reduce (List.map2 merge) 
          //rip the index back out                     
          |> List.map (fun (i,_,z) -> (i,z)) 

//get first value as seed for fold
let x1,l1 = Seq.nth 0 mergedValsByPos

//transform list (i,[z]) into list of (x,y,[z])
//key: (l)ow, (h)igh, (s)et, (nx)-next x, (ns)-next set
let result = 
    mergedValsByPos 
    //first remove any positions where there are no values
    |> List.filter (fun el -> snd(el) <> [])
    //double capture on state so we can take all or part of it
    |> List.fold (fun (((l,h,s)::t) as prev) (nx,ns) -> 
                    //if [z] value hasn't changed, we just enlarge range
                    //  of current state (from (l,h) to (l,nx)) 
                    //  otherwise we add a new element (nx,nx,ns) to state
                    if s=ns then (l,nx,s)::t else (nx,nx,ns)::prev 
                ) [x1,x1,l1] //initial state from seed values
    |> List.rev //folded value is backwards (because of::), so reverse