def main() :
    timer = TimeStat()
    pntStater = PointStat()
    pnt_nums = PNT_NUM_LIST
    logging_format = "{method_name}-{pnt_num} done ."
    for pnt_num in pnt_nums :
        pnts = generate_pnts_in_random(pnt_num , is_static_random=False)
        pntStater.add_stat_pnts(pnts)
        timer.add_stat_pnt_num(pnt_num)

        # brute force 
        method_name = "brute force"
        timer.start_time_stat()
        convex_hull_pnts = find_convex_hull_bruteforce(pnts)
        #print convex_hull_pnts
        plot_pnts = ready_plot_pnts_bruteforce(convex_hull_pnts)
        timer.end_time_stat()
        timer.add_stat_brute_force_timecost(timer.get_time_cost())
        pntStater.add_stat_bruteforce_convex_hull(plot_pnts)
        logging.info(logging_format.format(**locals()) )

        # graham scan
        method_name = "gramham scan"
        timer.start_time_stat()
        convex_hull_pnts = find_convex_hull_grahamscan(pnts)
        plot_pnts = ready_plot_pnts_grahamscan(convex_hull_pnts)
        timer.end_time_stat()
        timer.add_stat_graham_scan_timecost(timer.get_time_cost())
        pntStater.add_stat_graham_convex_hull(plot_pnts)
        logging.info(logging_format.format(**locals()) )

        # divide conquer
        method_name = "divide conquer"
        timer.start_time_stat()
        convex_hull_pnts = find_convex_hull_dc(pnts)
        plot_pnts = ready_plot_pnts_dc(convex_hull_pnts)
        timer.end_time_stat()
        timer.add_stat_dc_timecost(timer.get_time_cost())
        pntStater.add_stat_dc_convex_hull(plot_pnts)
        logging.info(logging_format.format(**locals()) )

    timer.print_time_cost()
    pntStater.draw_stat(convex_hull_pnt_plot_config)
    timer.draw_stat()
    plot.show()
Пример #2
0
def main():
    timer = TimeStat()
    pntStater = PointStat()
    pnt_nums = PNT_NUM_LIST
    logging_format = "{method_name}-{pnt_num} done ."
    for pnt_num in pnt_nums:
        pnts = generate_pnts_in_random(pnt_num, is_static_random=False)
        pntStater.add_stat_pnts(pnts)
        timer.add_stat_pnt_num(pnt_num)

        # brute force
        method_name = "brute force"
        timer.start_time_stat()
        convex_hull_pnts = find_convex_hull_bruteforce(pnts)
        #print convex_hull_pnts
        plot_pnts = ready_plot_pnts_bruteforce(convex_hull_pnts)
        timer.end_time_stat()
        timer.add_stat_brute_force_timecost(timer.get_time_cost())
        pntStater.add_stat_bruteforce_convex_hull(plot_pnts)
        logging.info(logging_format.format(**locals()))

        # graham scan
        method_name = "gramham scan"
        timer.start_time_stat()
        convex_hull_pnts = find_convex_hull_grahamscan(pnts)
        plot_pnts = ready_plot_pnts_grahamscan(convex_hull_pnts)
        timer.end_time_stat()
        timer.add_stat_graham_scan_timecost(timer.get_time_cost())
        pntStater.add_stat_graham_convex_hull(plot_pnts)
        logging.info(logging_format.format(**locals()))

        # divide conquer
        method_name = "divide conquer"
        timer.start_time_stat()
        convex_hull_pnts = find_convex_hull_dc(pnts)
        plot_pnts = ready_plot_pnts_dc(convex_hull_pnts)
        timer.end_time_stat()
        timer.add_stat_dc_timecost(timer.get_time_cost())
        pntStater.add_stat_dc_convex_hull(plot_pnts)
        logging.info(logging_format.format(**locals()))

    timer.print_time_cost()
    pntStater.draw_stat(convex_hull_pnt_plot_config)
    timer.draw_stat()
    plot.show()
def find_convex_hull_dc(pnts):
    pnt_num = len(pnts)
    if pnt_num <= 3:
        # the function `sort_pnts_by_polar_angle_ccw` is a O(n lgn) , but because the pnts is just less than 4 , so
        # time costing is O(1) !!
        return sort_pnts_by_polar_angle_ccw(Point(
            0, 0), pnts)  # using (0,0) to as the origin point(polar point)
    ## DIVIDE

    # to find a x=m line to binary partition all the points !
    # using randomized_select , O(n)
    pnt_to_partition = randomized_select(pnts,
                                         pnt_num / 2 + 1,
                                         key=lambda p: p.x)
    left_part_pnts = [pnt for pnt in pnts if pnt.x <= pnt_to_partition.x
                      ]  # hence , pnt_to_partition is included in left part
    right_part_pnts = [pnt for pnt in pnts if pnt.x > pnt_to_partition.x]
    ## BIG BUG !!
    # 考虑特殊情况:
    #   1. 当pnt_to_partition中的横坐标全部相同时,左边的集合将始终不变!!这将导致无穷递归!!
    #   2. 当pnt_to_partition是其中横坐标最大的点!此时,同样将造成左边集合不变。
    #   最终爆栈!!
    # !!如果将left_part_pnts的条件改为 < , 而right_part_pnt改为 >= , 情况2是否就好了?
    #     不是,因为这会在pnt_to_partition为横坐标最小的点时造成右边集合不变。所以这不是问题的关键!
    # 所以,首先需要特别检测一下,pnt_to_partition 的大小 以及 原始的点集 pnts的大小!
    # 如果没有改变,则说明遇到特殊情况,不能再递归!!(否则爆栈!)
    # 这时,有必要分开讨论这两种情况!
    # 1. 横坐标全部相同
    #   必然有 ===> y值最大和最小点为为该点集下的凸包!!
    #   故 直接返回y-min的最大点和最小点。(按照逆时针排序,应该先最小,再最大!)
    # 2. 选择的中位数横坐标是横坐标的最大值
    #   可以入上面思考中那样,将left_part_pnts改为 < , 将right_part_ptn 改为 >=
    if len(left_part_pnts) == len(pnts):
        #print pnts
        x = pnts[0].x
        is_x_all_same = True
        for pnt in pnts[1:]:
            if x != pnt.x:
                is_x_all_same = False
                break
        if is_x_all_same:
            min_y_pnt = min(pnts, key=lambda p: p.y)
            max_y_pnt = max(pnts, key=lambda p: p.y)
            return [min_y_pnt, max_y_pnt]
        else:
            left_part_pnts = [
                pnt for pnt in pnts if pnt.x < pnt_to_partition.x
            ]
            right_part_pnts = [
                pnt for pnt in pnts if pnt.x >= pnt_to_partition.x
            ]

    ## CONQUER
    left_part_convex_hull_pnts = find_convex_hull_dc(left_part_pnts)
    right_part_convex_hull_pnts = find_convex_hull_dc(right_part_pnts)

    ## MERGE

    # first , split all the covex hull points to 3 points sequence , every one has a order of `increasing of polar angle`
    # 为了说得更加明白,这里使用中文论述:
    # 左部分的凸包点、和右部分的凸包点,可以看做是各自是相对于自己的极点按照逆时针排序的。
    # 我们现在想要利用这种本身的有序性,使其在 共同的极点 下按照逆时针排序!
    # 按照PPT的内容,我们将共同的极点选作左边凸包的内点!
    # 这样,相对于该极点,左边的凸包肯定是按逆时针有序的了。但是右边的点却不是这样!
    # 我们将右边的凸包点分为两个部分,保证这两个部分内部是逆时针排序的!
    # 具体做法是,找到右边凸包中以左极点为原点极角最大和最小的点,记为 R_max , R_min
    # 以此两点为界,可以将右凸包分为两个部分 ——> 逆时针 R_min -> R_min ,且包含此两点;
    #                                           顺时针, R_min -> R_max , 不包含此两点。
    # 由于右边的凸包已经是相对于自身极点逆时针排序的,所以上述分割是O(n)的。

    # 此外,还有特别的情况——
    # 当左边不够3个时,就没有内点! 此时我们以右边凸包的内点为准点。(此时,我们可以保证右边凸包必然至少3个顶点)
    # 以右边凸包内点为极点,则只需将左边的原来相对于自身极点逆时针的凸包点逆序,即能保证其相对于右边极点是逆时针有序的。
    # 这个可以通过右手定则直观的得到。

    # 以上,就定义了对左右凸包点的划分逻辑。
    # 我们定义 sub_convex_hull_1 , sub_convex_hull_2 , sub_convex_hull_3来表示3个序列。
    # 这样可使代码更加简洁。但是逻辑可能梢混乱,所以以此作为说明。

    if len(left_part_convex_hull_pnts) < 3:
        if len(right_part_convex_hull_pnts) < 3:
            # 此情况下,左右都只有少于3个点。
            # 这时,可以以(0,0)为极点,左右凸包相对原点都是逆时针的!
            polar_pnt = Point(0, 0)
            sub_convex_hull_1 = left_part_convex_hull_pnts
            sub_convex_hull_2 = right_part_convex_hull_pnts
            sub_convex_hull_3 = []
        else:
            polar_pnt = generate_inner_pnt(right_part_convex_hull_pnts)
            # right
            sub_convex_hull_1 = right_part_convex_hull_pnts
            # left
            sub_convex_hull_2 = left_part_convex_hull_pnts
            sub_convex_hull_2.reverse()
            # just for the program struct
            sub_convex_hull_3 = []
    else:
        polar_pnt = generate_inner_pnt(left_part_convex_hull_pnts)
        # left
        sub_convex_hull_1 = left_part_convex_hull_pnts
        ## part right convex hull
        # first , find the min and max polar angle point according to the polar_pnt
        if len(right_part_convex_hull_pnts) < 3:
            # when right part convex hull just has less than 3 points , the are CCW according to the
            # left polar point
            sub_convex_hull_2 = right_part_convex_hull_pnts
            sub_convex_hull_3 = []
        else:
            angle_min_pnt, angle_max_pnt, angle_min_pnt_idx, angle_max_pnt_idx = (
                get_min_and_max_value_and_index(right_part_convex_hull_pnts))
            # right 1 , right 2
            if angle_max_pnt_idx > angle_min_pnt_idx:
                sub_convex_hull_2 = right_part_convex_hull_pnts[
                    angle_min_pnt_idx:angle_max_pnt_idx + 1]
                sub_convex_hull_3 = (
                    right_part_convex_hull_pnts[angle_max_pnt_idx + 1:] +
                    right_part_convex_hull_pnts[0:angle_min_pnt_idx])
                sub_convex_hull_3.reverse()
            else:
                sub_convex_hull_2 = (
                    right_part_convex_hull_pnts[angle_min_pnt_idx:] +
                    right_part_convex_hull_pnts[:angle_max_pnt_idx + 1])
                sub_convex_hull_3 = right_part_convex_hull_pnts[
                    angle_max_pnt_idx + 1:angle_min_pnt_idx]
                sub_convex_hull_3.reverse()
    # merge convex hull points
    merge_rst = merge_sub_convex_hull_pnts(sub_convex_hull_1,
                                           sub_convex_hull_2, polar_pnt)
    merge_rst = merge_sub_convex_hull_pnts(merge_rst, sub_convex_hull_3,
                                           polar_pnt)
    #print merge_rst
    #exit(1)
    return find_convex_hull_grahamscan(merge_rst)
def find_convex_hull_dc(pnts) :
    pnt_num = len(pnts)
    if pnt_num <= 3 :
        # the function `sort_pnts_by_polar_angle_ccw` is a O(n lgn) , but because the pnts is just less than 4 , so
        # time costing is O(1) !!
        return sort_pnts_by_polar_angle_ccw(Point(0,0) , pnts) # using (0,0) to as the origin point(polar point)
    ## DIVIDE

    # to find a x=m line to binary partition all the points ! 
    # using randomized_select , O(n)
    pnt_to_partition =  randomized_select(pnts , pnt_num / 2 + 1 , key=lambda p : p.x )
    left_part_pnts = [pnt for pnt in pnts if pnt.x <= pnt_to_partition.x ] # hence , pnt_to_partition is included in left part
    right_part_pnts = [pnt for pnt in pnts if pnt.x > pnt_to_partition.x ]
    ## BIG BUG !!
    # 考虑特殊情况:
    #   1. 当pnt_to_partition中的横坐标全部相同时,左边的集合将始终不变!!这将导致无穷递归!!
    #   2. 当pnt_to_partition是其中横坐标最大的点!此时,同样将造成左边集合不变。
    #   最终爆栈!!
    # !!如果将left_part_pnts的条件改为 < , 而right_part_pnt改为 >= , 情况2是否就好了?
    #     不是,因为这会在pnt_to_partition为横坐标最小的点时造成右边集合不变。所以这不是问题的关键!
    # 所以,首先需要特别检测一下,pnt_to_partition 的大小 以及 原始的点集 pnts的大小!
    # 如果没有改变,则说明遇到特殊情况,不能再递归!!(否则爆栈!)
    # 这时,有必要分开讨论这两种情况!
    # 1. 横坐标全部相同
    #   必然有 ===> y值最大和最小点为为该点集下的凸包!!
    #   故 直接返回y-min的最大点和最小点。(按照逆时针排序,应该先最小,再最大!)
    # 2. 选择的中位数横坐标是横坐标的最大值
    #   可以入上面思考中那样,将left_part_pnts改为 < , 将right_part_ptn 改为 >= 
    if len(left_part_pnts) == len(pnts) : 
        #print pnts
        x = pnts[0].x
        is_x_all_same = True
        for pnt in pnts[1:] :
            if x != pnt.x :
                is_x_all_same = False
                break
        if is_x_all_same :
            min_y_pnt = min(pnts , key=lambda p : p.y )
            max_y_pnt = max(pnts , key=lambda p : p.y )
            return [min_y_pnt , max_y_pnt]
        else :
            left_part_pnts =  [pnt for pnt in pnts if pnt.x < pnt_to_partition.x ]
            right_part_pnts = [pnt for pnt in pnts if pnt.x >= pnt_to_partition.x ]


    ## CONQUER
    left_part_convex_hull_pnts = find_convex_hull_dc(left_part_pnts)
    right_part_convex_hull_pnts = find_convex_hull_dc(right_part_pnts)
    
    ## MERGE

    # first , split all the covex hull points to 3 points sequence , every one has a order of `increasing of polar angle`
    # 为了说得更加明白,这里使用中文论述:
    # 左部分的凸包点、和右部分的凸包点,可以看做是各自是相对于自己的极点按照逆时针排序的。
    # 我们现在想要利用这种本身的有序性,使其在 共同的极点 下按照逆时针排序!
    # 按照PPT的内容,我们将共同的极点选作左边凸包的内点!
    # 这样,相对于该极点,左边的凸包肯定是按逆时针有序的了。但是右边的点却不是这样!
    # 我们将右边的凸包点分为两个部分,保证这两个部分内部是逆时针排序的!
    # 具体做法是,找到右边凸包中以左极点为原点极角最大和最小的点,记为 R_max , R_min 
    # 以此两点为界,可以将右凸包分为两个部分 ——> 逆时针 R_min -> R_min ,且包含此两点;
    #                                           顺时针, R_min -> R_max , 不包含此两点。
    # 由于右边的凸包已经是相对于自身极点逆时针排序的,所以上述分割是O(n)的。

    # 此外,还有特别的情况——
    # 当左边不够3个时,就没有内点! 此时我们以右边凸包的内点为准点。(此时,我们可以保证右边凸包必然至少3个顶点)
    # 以右边凸包内点为极点,则只需将左边的原来相对于自身极点逆时针的凸包点逆序,即能保证其相对于右边极点是逆时针有序的。
    # 这个可以通过右手定则直观的得到。

    # 以上,就定义了对左右凸包点的划分逻辑。
    # 我们定义 sub_convex_hull_1 , sub_convex_hull_2 , sub_convex_hull_3来表示3个序列。
    # 这样可使代码更加简洁。但是逻辑可能梢混乱,所以以此作为说明。

    if len(left_part_convex_hull_pnts) < 3 :
        if len(right_part_convex_hull_pnts) < 3 :
            # 此情况下,左右都只有少于3个点。
            # 这时,可以以(0,0)为极点,左右凸包相对原点都是逆时针的!
            polar_pnt = Point(0,0)
            sub_convex_hull_1 = left_part_convex_hull_pnts
            sub_convex_hull_2 = right_part_convex_hull_pnts
            sub_convex_hull_3 = []
        else :
            polar_pnt = generate_inner_pnt(right_part_convex_hull_pnts)
            # right 
            sub_convex_hull_1 = right_part_convex_hull_pnts
            # left
            sub_convex_hull_2 = left_part_convex_hull_pnts
            sub_convex_hull_2.reverse()
            # just for the program struct
            sub_convex_hull_3 = []
    else :
        polar_pnt = generate_inner_pnt(left_part_convex_hull_pnts)
        # left 
        sub_convex_hull_1 = left_part_convex_hull_pnts
        ## part right convex hull
        # first , find the min and max polar angle point according to the polar_pnt
        if len(right_part_convex_hull_pnts) < 3 :
            # when right part convex hull just has less than 3 points , the are CCW according to the
            # left polar point 
            sub_convex_hull_2 = right_part_convex_hull_pnts
            sub_convex_hull_3 = []
        else :
            angle_min_pnt , angle_max_pnt , angle_min_pnt_idx , angle_max_pnt_idx  = ( 
                                          get_min_and_max_value_and_index(right_part_convex_hull_pnts) )
            # right 1 , right 2
            if angle_max_pnt_idx > angle_min_pnt_idx :
                sub_convex_hull_2 = right_part_convex_hull_pnts[angle_min_pnt_idx : angle_max_pnt_idx + 1]
                sub_convex_hull_3 = ( right_part_convex_hull_pnts[angle_max_pnt_idx + 1 :] + 
                                      right_part_convex_hull_pnts[0 : angle_min_pnt_idx] )
                sub_convex_hull_3.reverse()
            else :
                sub_convex_hull_2 = ( right_part_convex_hull_pnts[angle_min_pnt_idx :] + 
                                      right_part_convex_hull_pnts[:angle_max_pnt_idx +1 ] )
                sub_convex_hull_3 = right_part_convex_hull_pnts[angle_max_pnt_idx + 1 : angle_min_pnt_idx]
                sub_convex_hull_3.reverse()
    # merge convex hull points
    merge_rst = merge_sub_convex_hull_pnts(sub_convex_hull_1 , sub_convex_hull_2 , polar_pnt)
    merge_rst = merge_sub_convex_hull_pnts(merge_rst , sub_convex_hull_3 , polar_pnt)
    #print merge_rst
    #exit(1)
    return find_convex_hull_grahamscan(merge_rst)