【问题标题】:CountNonDivisible - Codility training taskCountNonDivisible - Codility 训练任务
【发布时间】:2014-02-10 04:43:46
【问题描述】:

我现在正在学习 codility。有些任务我可以自己解决,但有些任务有问题。 这个任务的难度是。它是中等的,但我停滞不前了。

问题:


给定一个由 N 个整数组成的非空零索引数组 A。 对于每个满足 0 ≤ i

A[0] = 3
A[1] = 1
A[2] = 2
A[3] = 3
A[4] = 6

对于以下元素:

A[0] = 3, the non-divisors are: 2, 6,
A[1] = 1, the non-divisors are: 3, 2, 3, 6,
A[2] = 2, the non-divisors are: 3, 3, 6,
A[3] = 3, the non-divisors are: 2, 6,
A[6] = 6, there aren't any non-divisors.

写一个函数:

class Solution { public int[] solution(int[] A); }

给定一个由 N 个整数组成的非空零索引数组 A,返回一个整数序列,表示非除数的数量。 该序列应返回为:

  • 结构结果(在 C 中),
  • 或整数向量(在 C++ 中),
  • 或记录结果(帕斯卡),
  • 或整数数组(在任何其他编程语言中)。

例如,给定:

A[0] = 3
A[1] = 1
A[2] = 2
A[3] = 3
A[4] = 6

该函数应返回 [2, 4, 3, 2, 0],如上所述。 假设:

  • N 是 [1..50,000] 范围内的整数;
  • 数组 A 的每个元素都是 [1..2 * N] 范围内的整数。

复杂性:

  • 预计最坏情况时间复杂度为 O(N*log(N));
  • 预期的最坏情况空间复杂度为 O(N),超出输入存储 (不包括输入参数所需的存储空间)。

输入数组的元素可以修改。


我已经写了一些解决方案。但是我的解决方案体积庞大,并且仍然具有 O(n^2) 复杂性。 你能帮我一些想法或算法如何以最佳方式做到这一点吗?这不是面试任务或其他什么。我只是在训练并尝试解决所有任务。 您可以在此处找到此任务:http://codility.com/demo/train/ 第 9 课,课程中的第一个任务。

谢谢!

【问题讨论】:

  • 这听起来你应该把你的解决方案发布到Code Review 看看他们怎么说。
  • 我的第一个想法是玩一下 Eratosthenes 的筛子,看看你是否可以通过按摩它来解决这个问题。我不是说这就是答案。我不知道答案是什么。这正是我的第一个想法。
  • @Keppil,我的解决方案很简单。用一些拐杖来解决问题以降低复杂性是显而易见的方法,但它不起作用。我没有一个好主意,所以我想专注于想法和算法,而不是代码。

标签: java arrays algorithm training-data


【解决方案1】:

我想我会用 C++ 分享我的解决方案,获得 100 分。我认为这很简单。

https://codility.com/demo/results/demoQFK5R5-YGD/

  1. 首先计算数组中每个数字的出现次数。

  2. 然后对于每个数组元素i,它会在1到sqrt(i)的范围内找到其除数的数量,包括除数的结果。

  3. 最后,它从数组中的元素总数中减去给定元素的除数总数。

    vector<int> solution(vector<int> &A) {
    
        int N = A.size();
        vector<int> counts (*std::max_element(A.begin(), A.end()) + 1,0);
    
        // Calculate occurences of each number in the array
        for (int i = 0; i < N; ++i)
        {
            counts[A[i]] += 1;
        }
    
        std::vector<int> answer(N,0);
    
        // For each element of the array
        for (int i = 0; i < N; ++i)
        {
            // Calulate how many of its divisors are in the array
            int divisors = 0;
    
            for (int j = 1; j * j <= A[i]; ++j)
            {
                if (A[i] % j == 0)
                {
                    divisors += counts[j];
                    if (A[i] / j != j)
                    {
                        divisors += counts[A[i] / j];
                    }
                }
            }
    
            // Subtract the number of divisors from the number of elements in the array
            answer[i] = N - divisors;
        }
    
        return answer;
    }
    

【讨论】:

  • 看起来这个解决方案的复杂度是O(N*sqrt(N))。外循环 - N 迭代,内循环 - 最多 sqrt(2*N) 迭代。
  • 干得好!很好的解决方案
【解决方案2】:

此解决方案给出 100 分。 https://codility.com/demo/results/demo63KVRG-Q63/

public int[] solution(int[] A) {
    int[][] D = new int[A.length*2 + 1][2];

    for (int i = 0; i < A.length; i++) {
        D[A[i]][0]++;
        D[A[i]][1] = -1;
    }

    for (int i = 0; i < A.length; i++) {
        if (D[A[i]][1] == -1) {
            D[A[i]][1] = 0;
            for (int j = 1; j <= Math.sqrt(A[i]) ; j++) {
                if (A[i] % j == 0 && A[i] / j != j) {
                    D[A[i]][1] += D[j][0];
                    D[A[i]][1] += D[A[i]/j][0];
                } else if (A[i] % j == 0 && A[i] / j == j) {
                    D[A[i]][1] += D[j][0];
                }
            }
        }
    }
    for (int i = 0; i < A.length; i++) {
        A[i] = A.length - D[A[i]][1];
    }
    return A;
}

感谢大家的帮助。

【讨论】:

  • 这在算法上与我的解决方案有什么不同吗? (如果不是,我至少可以说我的解决方案是为了理解这种方法,而你的解决方案是为了获得高分;-))。如果它不同(除了像 sqrt 优化这样的小事),对差异的解释会很好。
  • @Marco 不同之处在于除数搜索,我不存储所有除数集,只存储除数数。总的来说,我采用了您解决方案的基本思想。我说这对我有帮助。谢谢。
  • @Stepler : 你能帮我理解一下吗
【解决方案3】:

解决方案尝试:(已编辑,见下文)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

// Solution for Lesson 9, "CountNonDivisible"
// of http://codility.com/demo/train/
public class Solution
{
    public static void main(String[] args)
    {
        int A[] = new int[5];
        A[0] = 3;
        A[1] = 1;
        A[2] = 2;
        A[3] = 3;
        A[4] = 6;

        Solution s = new Solution();
        int B[] = s.solution(A);
        System.out.println("Input  : "+Arrays.toString(A));
        System.out.println("Result : "+Arrays.toString(B));
    }

    public int[] solution(int[] A)
    {
        Set<Integer> setA = asSet(A);
        List<Set<Integer>> divisors = computeDivisors(A.length * 2);
        int occurrences[] = computeOccurrences(A);
        int nonDivisors[] = new int[A.length];
        for (int i=0; i<A.length; i++)
        {
            int value = A[i];
            Set<Integer> d = divisors.get(value);
            int totalOccurances = 0;
            for (Integer divisor : d)
            {
                if (setA.contains(divisor))
                {
                    totalOccurances += occurrences[divisor];
                }
            }
            nonDivisors[i] = A.length-totalOccurances;
        }
        return nonDivisors;
    }


    /**
     * Returns a set containing all elements of the given array
     * 
     * Space: O(N)
     * Time: O(N)
     * 
     * @param A The input array
     * @return The set
     */
    private static Set<Integer> asSet(int A[])
    {
        Set<Integer> result = new HashSet<Integer>();
        for (int value : A)
        {
            result.add(value);
        }
        return result;
    }


    /**
     * Computes a list that contains for each i in [0...maxValue+1] a set
     * with all divisors of i. This is basically an "Eratosthenes Sieve". 
     * But in addition to setting the entries of a list to 'false' 
     * (indicating that the respective numbers are non-prime), this 
     * methods inserts the divisors into the corresponding set.
     *  
     * Space: O(N) (?)
     * Time: O(N*logN) (?)
     * 
     * @param maxValue The maximum value
     * @return The list 
     */
    private static List<Set<Integer>> computeDivisors(int maxValue)
    {
        List<Boolean> prime = new ArrayList<Boolean>();
        prime.addAll(Collections.nCopies(maxValue+1, Boolean.TRUE));
        List<Set<Integer>> divisors = new ArrayList<Set<Integer>>();
        for (int i = 0; i < maxValue + 1; i++)
        {
            Set<Integer> d = new HashSet<Integer>();
            d.add(1);
            d.add(i);
            divisors.add(d);
        }
        for (int i = 2; i <= maxValue; i++)
        {
            int next = i + i;
            while (next <= maxValue)
            {
                divisors.get(next).addAll(divisors.get(i));
                prime.set(next, Boolean.FALSE);
                next += i;
            }
        }
        return divisors;
    }

    /**
     * Computes an array of length 2*A.length+1, where each entry i contains
     * the number of occurrences of value i in array A
     * 
     * Space: O(N)
     * Time: O(N)
     * 
     * @param A The input array
     * @return The occurrences array
     */
    private static int[] computeOccurrences(int A[])
    {
        int occurances[] = new int[A.length * 2 + 1];
        for (int i=0; i<A.length; i++)
        {
            int value = A[i];
            occurances[value]++;
        }
        return occurances;
    }
}

数组中出现的数字的最大值被定义为 2*arrayLength。对于数组中可能出现的每个数字,它计算

  • 这个数的除数集(使用 Erathostenes Sieve)
  • 数字在数组中实际出现的频率

有了这些信息,人们就可以穿过阵列了。对于在数组中找到的每个值,可以查找一组除数,并计算所有除数的出现总数。结果就是简单的数组长度,减去这个除数出现的总数。

由于它仅使用 Erathostenes 筛进行计算(并且仅遍历每个数字的除数集,这也应该是 logN),因此它的最坏情况时间复杂度应该是 O(N*logN )。但我不完全确定存储复杂度是否真的可以被认为是严格的 O(N),因为对于 N 个数字中的每一个,它都必须存储一组除数。也许这可以通过结合一些方法以某种方式避免,但无论如何,存储也至少在 O(N*logN) 中。

编辑:异常来自仅存储不超过 A.length*2-1 的值的数组,现在已修复。此外,除数集没有正确计算,现在也应该修复。 除此之外,分析结果像

got      [8, 8, 9, 10, 6, 8, .. 
expected [8, 8, 9, 10, 6, 8, ..

并没有真正的帮助。也许这是“游戏”的一部分,但我现在不玩这个游戏。基本思想应该很清楚,我假设它现在可以正常工作,直到有人提出反例;-P 它仍然没有达到 O(N) 的存储复杂度,但我还没有想到一种可能的方法来彻底实现这一点......

【讨论】:

  • 你检查过你的解决方案吗?我试过了,codility 给了我这些结果codility.com/demo/results/demoHQAV4M-BQP
  • 添加了 EDIT,但没有 codility 帐户,不会在那里进行测试
  • 谢谢,没注意到。现在功能是正确的,但是 3 个“大”测试用例中的 2 个发生了超时。 (代码可以很容易地优化以缓解这个问题,但我不确定超时是否完全是由于......实用......实现,或者真的是由于渐近复杂性。事实上,' large' case pass 表示不是后者,但未详细验证)。 100 分中的 77 分,不过......
  • 我喜欢你的回答,感谢你的详细解释。它对我有帮助,今天我找到了一个得分 100 的解决方案。
  • 由于与每个元素相关联的除数的上限是 (n^(1/3)),所以遍历集合的最坏时间复杂度不应该是 O(n^(1 /3))?导致总时间复杂度为 O(n^(4/3))?
【解决方案4】:

这是我的 100 分 Python 解决方案。希望对其他人有所帮助。

def solution(A):
    ''' Solution for the CountNonDivisible by codility
        Author: Sheng Yu - codesays.com
    '''
    from math import sqrt

    A_max = max(A)
    A_len = len(A)

    # Compute the frequency of occurrence of each
    # element in array A
    count = {}
    for element in A:
        count[element] = count.get(element,0)+1

    # Compute the divisors for each element in A
    divisors = {}
    for element in A:
        # Every nature number has a divisor 1.
        divisors[element] = [1]
    # In this for loop, we only find out all the
    # divisors less than sqrt(A_max), with brute
    # force method.
    for divisor in xrange(2, int(sqrt(A_max))+1):
        multiple = divisor
        while multiple  <= A_max:
            if multiple in divisors and not divisor in divisors[multiple]:
                divisors[multiple].append(divisor)
            multiple += divisor
    # In this loop, we compute all the divisors
    # greater than sqrt(A_max), filter out some
    # useless ones, and combine them.
    for element in divisors:
        temp = [element/div for div in divisors[element]]
        # Filter out the duplicate divisors
        temp = [item for item in temp if item not in divisors[element]]
        divisors[element].extend(temp)

    # The result of each number should be, the array length minus
    # the total number of occurances of its divisors.
    result = []
    for element in A:
        result.append(A_len-sum([count.get(div,0) for div in divisors[element]]))

    return result

【讨论】:

  • 看来and not divisor in divisors[multiple] 是没有必要的。
【解决方案5】:

这里我们采用我 100% 接受的解决方案:

boolean[] result_;
public int[] solution(int[] A) {
int a[][] = new int[2*A.length +  1][2];
result_ = new boolean[2*A.length +  1];
for(int i : A) {
    ++a[i][0];
}
a[1][1] = A.length - a[1][0];
result_[1] = true;
for(int i : A) {
    multCount(a,A,i);
}
int[] result = new int[A.length];
for(int i=0;i<result.length; i++) {
    result[i] = a[A[i]][1];
}
return result;

}

private void multCount( int[][] a, int[] A, int item) {
if( result_[item] )
    return;
int sub=(int)Math.sqrt(item);
  a[item][1] = A.length;
if(item % sub == 0&& sub >1){

    a[item][1] -=  a[sub][0];
    int rest = item/sub;
    if(rest != sub) {

        a[item][1] -=  a[rest][0];
    }
}

 a[item][1] -= a[item][0];
 a[item][1] -= a[1][0];
for(int i=2; i<sub; i++) {
    if(item % i == 0) {
        a[item][1] -= a[i][0];

        int rest = item/i;

        a[item][1] -=  a[rest][0];

    }
}
result_[item] = true;
   }

https://codility.com/demo/results/trainingZ2VRTK-5Y9/

【讨论】:

  • 您能解释一下您的代码,以便您的答案对其他人更有用吗?
  • 所有数字都在 1 到 2*N 之间
  • 所有数字都在 1 到 2*N 之间 我们创建了一个有两个维度的数组 数组的索引是数字本身 第二维是一个有两个数字的数组 第一个是计数数字的第二个是答案第一个数字直接由这个数组填充 for(int i : A) { ++a[i][0]; } 示例:假设数组 A 有以下项: [1,3,1,5,3] 二维数组将按如下方式填充 {{0,0},{2,0},{0,0} ,{2,0},{0,0},{1,0},{0,},..} 表示:数字 0 有 0 个 count 数字 1 有 2 个 count 数字 2 有 0 个 count 数字 3 有两个计数
  • 为了找到可设计的数字,我使用了最简单的算法:我应该找到 1 和 sqrt 之间的数字,并将它们添加到结果示例:
  • {3,1,3,2,6} 数组如下:{0,0}{1,0}{1,0}{2,0},{0,0} ,{0,0},{1.0} 让我们从第一项 3 开始:只有 3 和 1 这意味着不可设计的是数组的长度 (5 ) 减去 1 的数量和 3 表示 2为了消除多次重新计算的次数,我添加了布尔数组,告诉我这个数字是计算出来的
【解决方案6】:

我使用了一个哈希图,它满足 o(nlogn) 和 o(n) 时间和空间复杂度

import java.util.*;

class Solution {

    public int[] solution(int[] A) {

        int N = A.length;
        HashMap<Integer, Integer> count = new HashMap<>();

        for (int i : A) {
            Integer key = count.get(i);
            if (key != null) {
                count.put(i, key + 1);
            } else {
                count.put(i, 1);
            }
        }

        HashMap<Integer, Integer> divs = new HashMap<>();
        for (Integer n : count.keySet()) {
            int sum = 0;
            int j = 1;
            while (j * j <= n) {
                if (n % j == 0) {
                    if (count.containsKey(j)) {
                        sum += count.get(j);
                    }
                    //find n = j*k cases to add both to the dividors
                    int k = n / j;
                    if (k != j) {
                        if (count.containsKey(k)) {
                            sum += count.get(k);
                        }
                    }
                }
                j++;
            }

            divs.put(n, N - sum);
        }

        for (int i = 0; i < A.length; i++) {
            A[i] = divs.get(A[i]);
        }

        return A;
    }
}

【讨论】:

    【解决方案7】:

    100% 得分解决方案,ES6:

    function solution(A) {
    
      let count = {}
    
      // counting number frequency
      A.map(a => {
        //console.log(count[a])
        if (count[a] > 0) {
    
          count[a] = count[a] + 1
    
        } else {
    
          count[a] = 1
        }
    
      })
    
      // console.log(count)
    
      let divs = {}
    
      Object.keys(count).map(key => {
    
        let sum = 0
        let j = 1
    
        while (j * j <= key) {
    
          if (key % j == 0) {
    
            if (count[j] > 0) {
    
              sum += count[j]
            }
    
            // adding another dividor
            let k = key / j
    
            // scenario: 9 = 81 / 9. Escaping same number
    
            if (k != j) {
    
              if (count[k] > 0) {
    
                sum += count[k]
              }
            }
    
            // another possible solution: sum = sum * 2
            // if key is square number: sum -= 1
          }
    
          j++
        }
    
        divs[key] = A.length - sum
      })
      //    console.log(divs)
      let answer = []
      A.map(a => {
    
        answer.push(divs[a])
      })
      //    console.log(answer)
    
      return answer
    }
    

    受@Soley 解决方案的启发。

    【讨论】:

      【解决方案8】:

      Ruby 解决方案,100%

      def solution(a)
        elements = a.inject(Hash.new(0)) {|acc, el| acc[el] +=1;acc }
        n = elements.keys.sort
      
        div = n.each.inject(Hash.new(0)) do |acc, el|
          k=el
          while k < n[-1]
            k+=el
            acc[k] += elements[el]
          end
          acc
        end
      
        a.map {|x|  a.size - elements[x] - div[x] }
      end
      

      【讨论】:

        【解决方案9】:

        这个 100/100 的 C# 代码解决方案。 因为 C# 和 Java 非常相似可能会有所帮助。

         public class NonDivisiblesCounter
        {
            /// <summary>
            /// 1. Count the ocurrences of each element
            /// 2. Count all divisors for each element and subtract by the Length of array A to get nonDivisors
            /// 3. Add it to a cache since the elements can repeat and you do not need to calculate again.
            /// </summary>
            /// <param name="A"></param>
            /// <returns></returns>
            public int[] Count(int[] A)
            {
                int n = A.Length;
                var ocurrencesOfEach = CountOcurrencesOfEach(A);
                var nonDivisorsOfEach = new int[n];
                var nonDivisorsCache = new Dictionary<int, int>();
        
                for (int i = 0; i < n; i++)
                {
                    int element = A[i];
        
                    if (nonDivisorsCache.ContainsKey(element))
                    {
                        nonDivisorsOfEach[i] = nonDivisorsCache[element];
                    }
                    else
                    {
                        int nonDivisorCounter = n - CountDivisorsPerOcurrence(element, ocurrencesOfEach);
                        nonDivisorsOfEach[i] = nonDivisorCounter;
                        nonDivisorsCache[element] = nonDivisorCounter;
                    }
                }
        
                return nonDivisorsOfEach;
            }
        
            private int CountDivisorsPerOcurrence(int element, Dictionary<int, int> ocurrencesOfEach)
            {
                int square = (int)Math.Sqrt(element);
                int divisorCounter = 0;
        
                if (square * square == element && ocurrencesOfEach.ContainsKey(square))
                {
                    divisorCounter += ocurrencesOfEach[square];
                }
        
                for (int divisor = 1; element / divisor > square; divisor++)
                {
                    if (element % divisor == 0)
                    {
                        if (ocurrencesOfEach.ContainsKey(divisor))
                        {
                            divisorCounter += ocurrencesOfEach[divisor];
                        }
        
                        if (ocurrencesOfEach.ContainsKey(element / divisor))
                        {
                            divisorCounter += ocurrencesOfEach[element / divisor];
                        }
                    }
                }
        
                return divisorCounter;
            }
        
            private Dictionary<int, int> CountOcurrencesOfEach(int[] elements)
            {
                var result = new Dictionary<int, int>();
        
                for (int i = 0; i < elements.Length; i++)
                {
                    int element = elements[i];
        
                    if (result.ContainsKey(element))
                    {
                        result[element]++;
                    }
                    else
                    {
                        result.Add(element, 1);
                    }
                }
        
                return result;
            }
        }
        

        【讨论】:

          【解决方案10】:

          因为返回数字代表非除数的数量! 对于索引 [0],有 2 个非除数,对于索引 [3],也有 2 个非除数。

          【讨论】:

            【解决方案11】:

            这对我很有用,C 分数为 100%

            struct Results solution(int A[], int N) {
                struct Results result;
                // write your code in C99
            
                int *numbers = (int *)calloc(2*N + 1, sizeof(int));
                for (int i = 0; i < N; i++) {
                    ++numbers[A[i]];
                }
            
                int *numbers2 = (int *)calloc(2*N + 1, sizeof(int));
                for (int i = 0; 2*i < 2*N + 1; i++) {
                    if (numbers[i] != 0) {
                        for (int j = 2*i; j < 2*N + 1; j+=i) {
                            numbers2[j] += numbers[i];
                        }
                    }
                }
            
            
                int * Carr = (int *)calloc(N, sizeof(int));
            
                for (int i = 0; i < N; i++) {
                    Carr[i] = N - numbers[A[i]] - numbers2[A[i]];
                }
            
            
                result.C = Carr;
                result.L = N;
            
                free(numbers);
                free(numbers2);
                return result;
            }
            

            【讨论】:

              【解决方案12】:

              100% 用于 Javascript。 https://codility.com/demo/results/demoKRRRPF-8JW/

              function solution(A) {
                  var N = A.length;
                  if (N < 1 || N > 50000) throw 'Error: Bad input';
              
                  var uniqueDict = {};
                  var keys = [];
                  for (var i = 0; i < N; ++i) {
                      var num = A[i]
                      var uniqueCount = uniqueDict[num];
                      if (uniqueCount > 0) {
                          uniqueDict[num] = uniqueCount + 1;
                      } else {
                          uniqueDict[num] = 1;
                          keys.push(num);
                      }
                  }
              
                  keys.sort(function(a,b){
                      return a-b;
                  });
              
                  for (i = keys.length-1; i >= 0; --i) {
                      num = keys[i];
                      var divisorCount = divisors(num, uniqueDict);
              
                      var nonDivisorCount = N - divisorCount;
                      uniqueDict[num] = nonDivisorCount;
                  }
              
                  for (i = 0; i < N; ++i) {
                      num = A[i];
                      A[i] = uniqueDict[num];
                  }
                  return A;
              }
              
              function divisors(num, uniqueDict) {
                  var count = 0;
                  var x = 1;
                  while (x * x <= num) {
                      if (parseInt(num/x) === num/x) { // is divisor
                          if (uniqueDict[num/x] > 0) {
                              count += uniqueDict[num/x];
                          }
                          if (num/x !== x && uniqueDict[x] > 0) {
                              count += uniqueDict[x];
                          }
                      }
                      x++;
                  }
                  return count;
              }
              

              【讨论】:

              • 你可以用parseInt(num/x) === num/x代替num % x === 0
              【解决方案13】:

              这是我在 javascript 中的解决方案。我认为它比以前的要容易一些,它适用于 O(n log n)。您可以在此处查看其他解决方案:https://marioqs.wordpress.com

              function solution(A) {
                  var N = A.length;
                  var count = [];
                  var i;
                  for (i = 0; i < 2*N+1; ++i){
                      count.push(0);
                  }
                  for (i = 0; i < N; ++i){
                      ++count[A[i]];
                  }
                  var divisors = [];
                  for (i = 0; i < 2*N+1; ++i){
                      divisors.push(0);
                  } //the actual code starts here, before it's just initialisation of variables.
                  i = 1;
                  var k;
                  while (i <= 2*N){
                      k = i;
                      while (k <= 2*N){
                          divisors[k] += count[i];
                          k += i;
                      }
                      ++i;
                  }
              
                  var result = [];
                  for (i = 0; i < N; ++i){
                      result.push(0);
                  }
                  for (i = 0; i < N; ++i){
                      result[i] = N - divisors[A[i]];
                  }
                  return result;
              }
              

              【讨论】:

                【解决方案14】:

                根据 jaho 的回答,我添加了缓存以避免相同的计算。

                这里是the codility result

                下面是我的 C 代码。

                #include <math.h>
                
                struct Results solution(int A[], int N) {
                    int maxA = 0, i, j, sqrtA;
                    int *counts, *cache;
                    struct Results result;
                    result.C = (int *) malloc(N*sizeof(int));
                    result.L = N;
                
                    // Grep the maximum.
                    for (i = 0; i < N; ++i) {
                        if (A[i] > maxA)
                            maxA = A[i];
                    }
                    ++maxA;
                
                    // Initialize some arrays.
                    counts = (int *) malloc(maxA*sizeof(int));
                    cache = (int *) malloc(maxA*sizeof(int));
                    for (i = 0; i < maxA; ++i) {
                        counts[i] = 0;
                        cache[i] = -1;
                    }
                
                    // Count A.
                    for (i = 0; i < N; ++i) {
                        counts[A[i]] += 1;
                    }
                
                    // Main computation begins.
                    for (i = 0; i < N; ++i) {
                        // If the answer is already computed, use it.
                        if (cache[A[i]] >= 0) {
                            result.C[i] = cache[A[i]];
                            continue;
                        }
                
                        // There's no existing answer, compute it.
                        cache[A[i]] = N;
                        sqrtA = (int) sqrt(A[i]);
                        for (j = 1; j <= sqrtA; ++j) {
                            if (A[i]%j == 0) {
                                cache[A[i]] -= counts[j];
                                if (j*j != A[i]) {
                                    cache[A[i]] -= counts[A[i]/j];
                                }
                            }
                        }
                        result.C[i] = cache[A[i]];
                    }
                
                    // Since Codility prohibits the system calls,
                    // below free commands are commented.
                    // free(counts);
                    // free(cache);
                    return result;
                }
                

                【讨论】:

                  【解决方案15】:
                  import static java.lang.Integer.max;
                  import java.util.Arrays;
                  
                  
                    public int[] solution(int[] A) {
                      final int N = A.length;
                      final int MAX_VALUE_TBL = 2*50000;
                      int[] r = new int[N];                     // result table
                      int[] AS_AV = new int[MAX_VALUE_TBL + 1]; // number of cell with values
                  
                      int[] AS_AR = new int[MAX_VALUE_TBL + 1]; // results yet counted for values
                      boolean[] b = new boolean[MAX_VALUE_TBL + 1]; // if value has been counted
                  
                      if (N == 1) return r;
                  
                      for (int i = 0; i < N; i++) {
                        int v = A[i];
                        AS_AV[v]++;
                      }
                  
                      for (int i = 0; i < N; i++) {
                        int cu_val = A[i];
                        if (!b[cu_val]) {
                          int am_div = getAmDivisors(cu_val, AS_AV);
                          r[i] = N - am_div;
                          b[cu_val] = true;
                          AS_AR[cu_val] = r[i];
                        } else {
                          r[i] = AS_AR[cu_val];
                        }
                      }
                      return r;
                    }
                  
                    private int getAmDivisors(int cu_val, int[] AS_AV) {
                      int r = 0;
                      int sqr = (int) Math.sqrt(cu_val);
                  
                      for (int divisor = sqr; divisor > 0; divisor--) {
                        if (cu_val % divisor == 0) {
                          r += AS_AV[divisor];
                          if (divisor * divisor != cu_val) {
                            r += AS_AV[cu_val / divisor];
                          }
                        }
                      }
                      return r;
                    }
                  

                  【讨论】:

                    【解决方案16】:

                    这里 100% python 使用 Sieve of Eratosthenes principal 来避免计算除数(或多个)超过一次,直到数组的最大值:

                    def solution(A):
                     N=len(A) 
                     num_non_divisors=[0]*N
                     if N<2:
                      return num_non_divisors
                     MaxVal=max(A)    
                    #Trivial cases
                     if MaxVal < 2:
                         return num_non_divisors
                     MinVal=min(A)
                     if MinVal==MaxVal:
                      return num_non_divisors    
                     Occur = [0] * (MaxVal + 1) 
                    #Occurences of e in A  
                     for e in A:
                          Occur[e]+=1
                    #Divisors of any element lower than MaxVal 
                     Divisors = [Occur[1]] * (MaxVal + 1) 
                    #DejaVu to avoid counting them more than once
                     DejaVu = [0] * (MaxVal + 1) 
                    
                     for e in A:
                         if e!=1 and DejaVu[e]==0:
                          Divisors[e]+=Occur[e]
                          DejaVu[e]+=1
                    
                     i = 2     
                     while (i * i <= MaxVal):
                    #We start at i x i to avoid counting 2 times multiples of the form k x i, where k<i.
                       k =  i * i
                       while (k <= MaxVal):
                         Divisors[k] += Occur[i]
                         if i * i < k: #equivalent k/i != i
                         #Symmetric divisor
                             Divisors[k] += Occur[int(k/i)];
                         k += i
                       i += 1
                    #Re-initialize DejaVu
                     DejaVu = [0] * (MaxVal + 1)  
                     for i in range(0,len(A)):
                        if not DejaVu[A[i]]: 
                         DejaVu[A[i]]=N-Divisors[A[i]]
                        num_non_divisors[i]=DejaVu[A[i]]
                     return num_non_divisors
                    

                    【讨论】:

                      【解决方案17】:

                      这是我的 java 解决方案,100%。

                      没有模,没有除法。只需“计数排序”并筛分即可。

                      public int[] solution(int[] A) {
                      
                          //find max number. To be used for 'count sort' array size.
                          int max = A[0];
                          for (int i = 1 ; i < A.length ; i++) {
                              max = Math.max(max, A[i]);
                          }
                      
                          //count sort
                          int [] count = new int [max+1];
                          for (int i = 0 ; i < A.length ; i++) {
                              count[A[i]]++;
                          }
                      
                          int [] nonDiv = new int [max+1];
                          //initially count all elements as non divisible (minus 'number of occurrences' of the the given number)
                          for (int i = 1 ; i < nonDiv.length; i++) {
                              if (count[i] != 0) {//skip numbers which don't exists in table A
                                  nonDiv[i] = A.length - count[i];
                              }
                          }
                      
                          //sieve
                          for (int i = 1 ; i < nonDiv.length; i++) {
                              if (count[i] != 0) {//skip numbers which don't exists in table A
                                  int s = i*2;
                                  while (s<nonDiv.length) {
                                      if (nonDiv[s] != 0) {
                                          //Sieve. Most important part. Decrease number of non-divisible by the number of occurrences of number 'i'.
                                          nonDiv[s] -= count[i];
                                      }
                                      s+=i;
                                  }
                              }
                          }
                      
                          //produce the output
                          int []res = new int [A.length];
                          for (int i = 0 ; i < A.length ; i++) {
                              res[i] = nonDiv[A[i]];
                          }
                      
                          return res;
                      
                      }
                      

                      【讨论】:

                        【解决方案18】:

                        Golang solution 100%,唯一不同的是我们必须使用 hashmap 来缓存除数,否则性能测试会部分失败。

                        package solution
                        
                        // you can also use imports, for example:
                        // import "fmt"
                        // import "os"
                        
                        // you can write to stdout for debugging purposes, e.g.
                        // fmt.Println("this is a debug message")
                        
                        func Solution(A []int) []int {
                            tdMapping := make(map[int]int)
                        
                            MaxValue := 2 * len(A)
                        
                            occurs := make([]int, MaxValue+1)
                            for _, v := range A {
                                occurs[v]++
                            }
                        
                            r := make([]int, len(A))
                        
                            for i := 0; i < len(A); i++ {
                                totalDivisors := 0
                                if _, ok := tdMapping[A[i]]; ok {
                                    totalDivisors = tdMapping[A[i]]
                                } else {
                                    for j := 1; j*j <= A[i]; j++ {
                                        if j*j == A[i] {
                                            totalDivisors += occurs[j]
                                        } else {
                                            if A[i]%j == 0 {
                                                totalDivisors += occurs[j] + occurs[A[i]/j]
                                            }
                                        }
                                    }
                                    tdMapping[A[i]] = totalDivisors
                                }
                        
                                r[i] = len(A) - totalDivisors
                            }
                        
                            return r
                        }
                        

                        【讨论】:

                          【解决方案19】:

                          得分为 100% 的 JavaScript 解决方案。 Codility 检测到复杂度为 O(nlogn),但实际上是 O(n * sqrt(n))

                          function solution(A) {
                            const createCounts = A => {
                              const counts = Array(A.length * 2 + 1).fill(0)
                              for (let i = 0; i < A.length; i++) {
                                counts[A[i]] += 1
                              }
                              return counts
                            }
                            const counts = createCounts(A)
                            const results = []
                            for (let i = 0; i < A.length; i++) {
                              let nonDivisiblesCount = A.length
                              let j = 1
                              while (j * j < A[i]) {
                                if (A[i] % j === 0) {
                                  nonDivisiblesCount -= counts[j]
                                  nonDivisiblesCount -= counts[A[i] / j]
                                }
                                j++
                              }
                              if (j * j === A[i]) {
                                nonDivisiblesCount -= counts[j]
                              }
                              results.push(nonDivisiblesCount)
                            }
                            return results
                          }
                          
                          const A = [3, 1, 2, 3, 6]
                          console.log(A)
                          const s = solution(A)
                          console.log(s)
                          

                          【讨论】:

                            【解决方案20】:

                            用 GO 语言编写的最难阅读的解决方案之一,由 Java 开发人员设计(总分 100%):

                            func Solution(A []int) []int {
                                aSize := len(A)
                                maxValue := A[0]
                                for idx := 0; idx < aSize; idx++ {
                                    element := A[idx]
                                    if maxValue < element {
                                        maxValue = element
                                    }
                                }
                            
                                remainDividersCountList := make([]int, maxValue+1)
                            
                                for idx := 0; idx < aSize; idx++ {
                                    element := A[idx]
                                    if remainDividersCountList[element] == 0 {
                                        remainDividersCountList[element] = aSize - 1
                                    } else {
                                        remainDividersCountList[element] = remainDividersCountList[element] - 1
                                    }
                                }
                                cachedResultMap := make([]int, maxValue+1)
                                alreadyCalculated := make([]int, maxValue+1)
                                alreadyCalculatedDuplicated := make([]int, maxValue+1)
                                caluclatedMap := make(map[int][]int)
                                for idx := 0; idx < aSize; idx++ {
                                    element := A[idx]
                                    if alreadyCalculated[element] == 0 {
                                        for multiplier := 2; multiplier <= maxValue/element; multiplier++ {
                                            multResult := element * multiplier
                                            if multResult > maxValue {
                                                break
                                            } else {
                                                cachedResult := cachedResultMap[multResult]
                                                if cachedResult > 0 {
                                                    cachedResultMap[multResult] = cachedResult + 1
                                                } else {
                                                    cachedResultMap[multResult] = 1
                                                }
                                                caluclatedMap[element] = append(caluclatedMap[element], multResult)
                                            }
                                        }
                                        alreadyCalculated[element] = 1
                                    } else if alreadyCalculatedDuplicated[element] == 0 {
                                        multiplier := aSize - (remainDividersCountList[element] + 1)
                                        list := caluclatedMap[element]
                                        for repIdx := 0; repIdx < len(list); repIdx++ {
                                            repElem := list[repIdx]
                                            cachedResultMap[repElem] = cachedResultMap[repElem] + (1 * multiplier)
                                        }
                                        alreadyCalculatedDuplicated[element] = 1
                                    }
                                }
                            
                                result := make([]int, aSize)
                                for idx := 0; idx < aSize; idx++ {
                                    element := A[idx]
                                    result[idx] = remainDividersCountList[element] - cachedResultMap[element]
                                }
                                return result
                            }
                            

                            【讨论】:

                              【解决方案21】:

                              性能不是此代码中最好的,但可读性非常简单。

                              Map<Integer, Long> map = IntStream.range(0, A.length)
                                          .collect(HashMap::new,
                                                  (acc, i) -> acc.compute(A[i], (k, v) -> v == null ? 1 : ++v),
                                                  HashMap::putAll);
                              
                                  int[] removed = new int[A.length];
                              
                                  for (int i = 0; i < A.length; i++) {
                                      int N = A[i];
                                      int max = N;
                                      List<Integer> divisors = new ArrayList<>();
                                      if (N == 1) {
                                          divisors.add(1);
                                      } else {
                                          for (int div = 1; div < max; div++) {
                                              if (N % div == 0) {
                                                  divisors.add(div);
                                                  if (div != N / div) {
                                                      divisors.add(N / div);
                                                  }
                                              }
                                              if (N / div < max) {
                                                  max = N / div;
                                              }
                                          }
                                      }
                                      removed[i] += map.entrySet().stream()
                                              .filter(entry -> divisors.stream().noneMatch(div -> Objects.equals(entry.getKey(), div)))
                                              .mapToLong(e -> e.getValue()).sum();
                              

                              【讨论】:

                                【解决方案22】:

                                // 在 Swift 4.2.1 (Linux) 中编写代码

                                public func solution(_ A : inout [Int]) -> [Int] {
                                
                                let n = A.count
                                
                                var counters = Array(repeating: 0, count: 2 * n + 1)
                                
                                var divisors = Array(repeating: 0, count: 2 * n + 2)
                                
                                var nonDivisors = Array(repeating: 0, count: n)
                                
                                for i in A {
                                    counters[i] += 1
                                }
                                
                                for i in 1...2 * n {
                                    if counters[i] > 0 {
                                        var k = i
                                
                                        while k <= 2 * n {
                                            if counters[k] > 0 {
                                                divisors[k] += counters[i]
                                            }
                                
                                            k += i
                                        }
                                    }
                                }
                                
                                for i in 0..<n {
                                    nonDivisors[i] = n - divisors[A[i]]
                                }
                                
                                return nonDivisors
                                

                                }

                                var 数组 = [3, 1, 2, 3, 6]

                                var a = 解决方案(&array)

                                【讨论】:

                                  【解决方案23】:

                                  一个易于理解的 100% python 解决方案 - 我认为 :-)

                                  首先你需要除数。

                                  def get_divisors(n):
                                      froot = int(n**.5)
                                      divs = set()
                                      # reverse through possible divisors which are lower than root(n)
                                      while froot > 0:
                                          if not n%froot:
                                              divs.add(froot)
                                              divs.add(n//froot) # Catch the higher divisor on the other side of froot
                                          froot-=1
                                      return divs
                                  

                                  然后您可以先计算每个 int 的频率,以便更容易计算非除数。将非除数放入 dict 中,然后简单地在最后的列表理解中检索答案。

                                  def solution(A):
                                      N = len(A)
                                      int_count = {}
                                      
                                      # O(N) scan to count number frequency
                                      for i in range(N):
                                          int_count[A[i]] = int_count.get(A[i], 0) + 1
                                      
                                      # Create an array for every i non-divisor count
                                      div_count = {}
                                      
                                      for i, freq in int_count.items():
                                          divs = get_divisors(i)
                                          num_divs = 0
                                          for d in divs:
                                              num_divs += int_count.get(d, 0)
                                          div_count[i] = N-num_divs # N -  divisors = non-divisors :-)
                                          
                                      return [div_count[A[i]] for i in range(N)]
                                  

                                  偶尔做一个利用python的解决方案很好:-)

                                  https://github.com/niall-oc/things/tree/master/codility

                                  【讨论】:

                                    【解决方案24】:

                                    要了解为什么数字“2”在以下结果 [2,4,3,2,0] 上出现两次,请参见以下代码:

                                    A[0] = 3, the non-divisors are: 2, 6       >> Quantity: 2
                                    A[1] = 1, the non-divisors are: 3, 2, 3, 6 >> Quantity: 4
                                    A[2] = 2, the non-divisors are: 3, 3, 6,   >> Quantity: 3
                                    A[3] = 3, the non-divisors are: 2, 6       >> Quantity: 2
                                    A[6] = 6, there aren't any non-divisors.   >> Quantity: 0
                                    

                                    【讨论】:

                                      【解决方案25】:

                                      O(n * logn) 下面的 C++ 解决方案。

                                      检查 A 中的最大元素以节省空间,而不是使用 sizeA * 2

                                      构建 A 中出现次数的哈希。

                                      将 {1, num} 添加为所有数字的除数。除数存储在 unordered_set 中以进行有效插入和查找。这些元素也将是独一无二的。

                                      将所有其他除数添加到所有数字。

                                      遍历 A 中的每个数字。检查除数在 A 中出现的次数。

                                      非除数将是 A 的长度减去找到的除数。

                                      vector<int> solution(vector<int> &A)                                            
                                      {                                                                               
                                        const int sizeA = A.size();                                                   
                                        const int max_elem = *max_element(A.cbegin(), A.cend());                      
                                        vector<int> hash(max_elem, 0);                                                
                                        vector<unordered_set<int>> divisors_hash(max_elem, unordered_set<int>{});     
                                        for (const int e : A) {                                                       
                                          ++hash[e - 1];                                                              
                                          divisors_hash[e - 1].insert({1, e});                                        
                                        }                                                                             
                                                                                                                      
                                        for (int i = 2; i * i <= max_elem; ++i) {                                     
                                          for (int k = i; k <= max_elem; k += i) {                                    
                                            if (hash[k - 1]) divisors_hash[k - 1].insert({i, k / i});                 
                                          }                                                                           
                                        }                                                                             
                                                                                                                      
                                        vector<int> non_divisors(sizeA, 0);                                           
                                        for (int i = 0; i < sizeA; ++i) {                                             
                                          const int e = A[i];                                                         
                                          int divisor_count = 0;                                                      
                                          for (const int divisor : divisors_hash[e - 1]) {                            
                                            divisor_count += hash[divisor - 1];                                       
                                          }                                                                           
                                          non_divisors[i] = sizeA - divisor_count;                                    
                                        }                                                                             
                                                                                                                      
                                        return non_divisors;                                                          
                                      }
                                      

                                      【讨论】:

                                        【解决方案26】:
                                        /**
                                         * Count Non-divisible
                                         */
                                        
                                        public class Solution {
                                        
                                            public int[] solution(int[] A) {
                                                int[] div = new int[A.length];
                                                for (int e = 0; e < div.length; e++) {
                                                    div[e] = 0;
                                                    for (int i = 0; i < A.length; i++) {
                                                        int dividend = A[e];
                                                        if (dividend % A[i] != 0) {
                                                            div[e] += 1;
                                                        }
                                                    }
                                                }
                                                return div;
                                            }
                                        
                                            public static void main(String args[]) {
                                                int[] A = new int[]{3, 1, 2, 3, 6};
                                                Solution s = new Solution();
                                                int[] B = s.solution(A);
                                                for (int i = 0; i < B.length; i++) {
                                                    System.out.println(B[i]);
                                                }
                                            }
                                        }
                                        

                                        【讨论】:

                                        • 解的复杂度为 n^2。 Codility 需要 n*log(n)。
                                        猜你喜欢
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 2016-12-10
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 2018-10-23
                                        • 1970-01-01
                                        • 2018-06-26
                                        相关资源
                                        最近更新 更多