“Approximate” greatest common divisor

前端 未结 8 1106
清歌不尽
清歌不尽 2020-12-13 17:11

Suppose you have a list of floating point numbers that are approximately multiples of a common quantity, for example

2.468, 3.700, 6.1699

w

相关标签:
8条回答
  • 2020-12-13 18:08

    I found this question looking for answers for mine in MathStackExchange (here and here).

    I've only managed (yet) to measure the appeal of a fundamental frequency given a list of harmonic frequencies (following the sound/music nomenclature), which can be useful if you have a reduced number of options and is feasible to compute the appeal of each one and then choose the best fit.

    C&P from my question in MSE (there the formatting is prettier):

    • being v the list {v_1, v_2, ..., v_n}, ordered from lower to higher
    • mean_sin(v, x) = sum(sin(2*pi*v_i/x), for i in {1, ...,n})/n
    • mean_cos(v, x) = sum(cos(2*pi*v_i/x), for i in {1, ...,n})/n
    • gcd_appeal(v, x) = 1 - sqrt(mean_sin(v, x)^2 + (mean_cos(v, x) - 1)^2)/2, which yields a number in the interval [0,1].

    The goal is to find the x that maximizes the appeal. Here is the (gcd_appeal) graph for your example [2.468, 3.700, 6.1699], where you find that the optimum GCD is at x = 1.2337899957639993

    Edit: You may find handy this JAVA code to calculate the (fuzzy) divisibility (aka gcd_appeal) of a divisor relative to a list of dividends; you can use it to test which of your candidates makes the best divisor. The code looks ugly because I tried to optimize it for performance.

        //returns the mean divisibility of dividend/divisor as a value in the range [0 and 1]
        // 0 means no divisibility at all
        // 1 means full divisibility
        public double divisibility(double divisor, double... dividends) {
            double n = dividends.length;
            double factor = 2.0 / divisor;
            double sum_x = -n;
            double sum_y = 0.0;
            double[] coord = new double[2];
            for (double v : dividends) {
                coordinates(v * factor, coord);
                sum_x += coord[0];
                sum_y += coord[1];
            }
            double err = 1.0 - Math.sqrt(sum_x * sum_x + sum_y * sum_y) / (2.0 * n);
            //Might happen due to approximation error
            return err >= 0.0 ? err : 0.0;
        }
    
        private void coordinates(double x, double[] out) {
            //Bhaskara performant approximation to
            //out[0] = Math.cos(Math.PI*x);
            //out[1] = Math.sin(Math.PI*x);
            long cos_int_part = (long) (x + 0.5);
            long sin_int_part = (long) x;
            double rem = x - cos_int_part;
            if (cos_int_part != sin_int_part) {
                double common_s = 4.0 * rem;
                double cos_rem_s = common_s * rem - 1.0;
                double sin_rem_s = cos_rem_s + common_s + 1.0;
                out[0] = (((cos_int_part & 1L) * 8L - 4L) * cos_rem_s) / (cos_rem_s + 5.0);
                out[1] = (((sin_int_part & 1L) * 8L - 4L) * sin_rem_s) / (sin_rem_s + 5.0);
            } else {
                double common_s = 4.0 * rem - 4.0;
                double sin_rem_s = common_s * rem;
                double cos_rem_s = sin_rem_s + common_s + 3.0;
                double common_2 = ((cos_int_part & 1L) * 8L - 4L);
                out[0] = (common_2 * cos_rem_s) / (cos_rem_s + 5.0);
                out[1] = (common_2 * sin_rem_s) / (sin_rem_s + 5.0);
            }
        }
    
    0 讨论(0)
  • 2020-12-13 18:11

    You can run Euclid's gcd algorithm with anything smaller then 0.01 (or a small number of your choice) being a pseudo 0. With your numbers:

    3.700 = 1 * 2.468 + 1.232,
    2.468 = 2 * 1.232 + 0.004. 
    

    So the pseudo gcd of the first two numbers is 1.232. Now you take the gcd of this with your last number:

    6.1699 = 5 * 1.232 + 0.0099.
    

    So 1.232 is the pseudo gcd, and the mutiples are 2,3,5. To improve this result, you may take the linear regression on the data points:

    (2,2.468), (3,3.7), (5,6.1699).
    

    The slope is the improved pseudo gcd.

    Caveat: the first part of this is algorithm is numerically unstable - if you start with very dirty data, you are in trouble.

    0 讨论(0)
提交回复
热议问题