仿照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是跳过第几个之后开始,取得的值包括当前下标值的内容
}
}
}
}