CountNonDivisible - Codility 训练任务

Posted

技术标签:

【中文标题】CountNonDivisible - Codility 训练任务【英文标题】:CountNonDivisible - Codility training task 【发布时间】: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,我的解决方案很简单。用一些拐杖来解决问题以降低复杂性是显而易见的方法,但它不起作用。我没有一个好主意,所以我想专注于想法和算法,而不是代码。 【参考方案1】:

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

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

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

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

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

    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,01,01,02,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)。

以上是关于CountNonDivisible - Codility 训练任务的主要内容,如果未能解决你的问题,请参考以下文章

the solution of CountNonDivisible by Codility