java – CountNonDivisible – Codility训练任务

我现在正在训练鳕鱼.我可以自己解决一些任务,但有些任务有问题.
该任务的难度是< **>.这是中等的,但我停滞不前.

问题:

您将获得一个由N个整数组成的非空零索引数组A.
对于每个数字A [i],使得0≤i<1. N,我们想要计算不是A [i]的除数的数组元素的数量.我们说这些元素是非除数.
例如,考虑整数N = 5和数组A,以便:

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课,课程中的第一项任务.

谢谢!

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

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的Sieve进行计算(并且只遍历每个数字的除数组,也应该是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)存储的复杂性,但我还没有考虑过彻底实现这一目标的可行方法……

翻译自:https://stackoverflow.com/questions/21243729/countnondivisible-codility-training-task

转载注明原文:java – CountNonDivisible – Codility训练任务