JAVA自然段点法

该博客介绍了一个用Java编写的自然段点法工具,仿照ArcMap的相应功能。代码首先对输入的数值进行排序和分组,然后通过计算平方偏差找到最优的分段点,最终返回这些分段点。示例中展示了如何使用该工具并计算运行时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

仿照ArcMap的自然段点法工具,编写java版本

import org.apache.commons.lang3.ArrayUtils;

import java.util.*;


/**
 * 自然段点法
 */
public class SectionPointUtils {
    public static void main(String[] args) {
        //double[] vals = {2.8942,1.7640,1.7640,1.8431,56.8261,12.0313,323.9466,271.6375,151.8535,973.2306,47.9283,16.6712,655.8221,653.4434,31.9215,128.6017,246.4876,363.6499,221.0955,60.0774,7.3471,137.4829,805.9642,1035.5554,971.2950,429.1342,1113.3559,53.8958,505.6593,468.3412,22.9725,175.8431,142.4774,689.7310,10.8726,465.5753,199.4416,195.4734,574.8076,986.5873,1433.0800,309.0351,354.5864,507.7732,540.4929,543.2156,435.8749,506.3104,2024.4939,1446.2230,1768.2030,1491.5064,776.1774,517.2411,1342.7609,140.1107,302.3475,700.3783,1378.5351,713.8047,684.6755,1490.7281,1463.0441,1023.3943,672.5716,921.9466,719.7841,424.9630,837.2289,906.6014,944.7877,740.9753,1063.4383,412.9952,465.6922,1021.1533,464.1352,874.7015,765.1950,697.1860,856.3603,512.7394,573.8054,759.9695,1205.2238,581.1831,676.2104,374.3139,480.8792,113.7231,495.6780,803.4577,866.1656,1089.1465,196.2495,786.7560,1175.1297,2063.0481,575.0976,890.4293,623.8260};

        //double[] vals = {2.8942,12.0313,323.9466,973.2306};


        double[] vals = {2.8942,12.0313,323.9466,12.456,973.2306};

        long start,end;
        start = System.currentTimeMillis();
        List<Double> d = GetBreaks(vals,5);
        for (int i = 0; i < d.size(); i++) {
            System.out.println(d.get(i));
        }
        end = System.currentTimeMillis();
        System.out.println("start time:" + start+ "; end time:" + end+ "; Run Time:" + (end - start) + "(ms)");
    }

    /**
     * 得到自然段点法的数值
     * vals内的唯一值数量不能小于count
     * @param vals
     * @param count
     * @return
     */
    public static List<Double> GetBreaks(double[] vals, int count) {

        Map<Double,ArrayList<Double>> group_vals = new LinkedHashMap<Double,ArrayList<Double>>();

        //排序
        Arrays.sort(vals);

        //分组
        //一个记录值,一个记录出现次数
        List<Double> star = new ArrayList<>();
        List<Integer> number = new ArrayList<>();

        //数字数量分组
        for(double i : vals){
            if(-1 != star.lastIndexOf(i)) {
                number.add(star.lastIndexOf(i), number.get(star.lastIndexOf(i))+1);
            }else {
                star.add(i);
                number.add(1);
            }
        }

        //换成map(分组的值当k,值是数值直接放进去)
        for(int i =0 ; i< star.size() ; i++) {
            ArrayList<Double> starValue = new ArrayList<>();
            for (int j = 0; j < number.get(i); j++) {
                starValue.add(star.get(i));
            }

            group_vals.put(star.get(i),starValue);
        }

        //所有K的有序集合
        ArrayList<Double> keyList = new ArrayList<Double>();
        for(double group_valsKey:group_vals.keySet()) {
            keyList.add(group_valsKey);
        }

        //map大小和要分几组
        int n = group_vals.size();
        int k = count;

        List<Integer[]> all_breaks = getAllBreaks(n - 1, k - 1);

        double min_sum_sqd = Double.MAX_VALUE;
        Integer[] optimal_breaks = null;

        for (Integer[] breaks : all_breaks) {
            List<Integer> break_positions = new ArrayList<Integer>();

            break_positions.add(0);
            break_positions.addAll(Arrays.asList(breaks));
            break_positions.add(n);

            double sum_sqd = 0.0;
            for (int i = 0; i < break_positions.size() - 1; i++) {
                int begin = break_positions.get(i);
                int gap = break_positions.get(i + 1) - begin;

                List class_vals = new ArrayList<Double>();
                for (int j = 0; j < gap; j++) {
                    int key_index = begin + j;

                    double key = keyList.get(key_index);

                    ArrayList<Double> key_vals = group_vals.get(key);

                    class_vals.addAll(key_vals);
                }

                sum_sqd += SquareDeviation(class_vals);
            }

            if (sum_sqd < min_sum_sqd) {
                min_sum_sqd = sum_sqd;
                optimal_breaks = breaks;
            }
        }
        List<Double> result = new ArrayList<Double>();
        for (int _break : optimal_breaks) {
            result.add(keyList.get(_break));
        }

        return result;
    }

    private static List<Integer[]> getAllBreaks(int n, int k){
        int[] r = new int[k + 1];
        List<Integer[]> rs = new ArrayList<>();
        combination(n, k, r, rs);
        return rs;
    }


    public static double SquareDeviation(List<Double> vals){
        Double mean = vals.stream().mapToDouble(Double::doubleValue).sum() / vals.size();
        Double doubles = 0.0;
        for (int i = 0; i < vals.size(); i++) {
            doubles += (vals.get(i) - mean) * (vals.get(i) - mean);
        }
        return doubles / vals.size();
    }

    private static void combination(int n, int k, int[] r, List<Integer[]> rs){
        for (int i = n; i >= k; i--){
            r[k] = i;
            if (k > 1){
                combination(i - 1, k - 1, r, rs);//递归
            }
            else{
                Integer[] removed = Arrays.stream(ArrayUtils.remove(r, 0)).boxed().toArray(Integer[]::new);
                rs.add(removed);
                //rs.add(r.Skip(1).ToArray());//Skip  跳过指定的数量的序列中的元素,然后返回剩余元素。 Skip是跳过第几个之后开始,取得的值包括当前下标值的内容
            }
        }
    }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值