Find all narcissistic (armstrong) numbers faster on Java - java

Is there more efficient way to do that?
Given number N - find all the narcissistic ( armstrong ) numbers that < N.
Here is my code, but I guess there is more efficient solutions. Also, probably, we could solve it through bit operation?
public static void main(String args[]) throws Exception
{
long a = System.nanoTime();
int t [] = getNumbers(4_483_647L);
long b = System.nanoTime();
System.out.println("Time totally "+(b-a));
for(int k: t)
System.out.print(k+" ");
}
public static int[] getNumbers(long N)
{
int length=1;
int porog=10, r=1, s=1;
double k;
LinkedList<Integer> list = new LinkedList<>();
for(int i=1; i<N; i++)
{
if(i==porog)
{
length++;
porog*=10;
}
s = i;
k=0;
while(s>0)
{
r = s%10;
k+=Math.pow(r, length);
if(k>i)break;
s=s/10;
}
if((int)k==i)
list.add(i);
}
int[] result = new int[list.size()];
int i=0;
for(int n: list)
{
result[i] = n;
i++;
}
return result; } }

Some observations:
If your initial maximum is a long, your results should be long types, too, just in case (int works for you as the narcissistic numbers are far apart)
If you change your return type to be a "big" Long, you can use Collections.toArray() to repack the results to an array...
...although really, you should just return the linked list...
You don't need to keep recalculating powers. For each decade in the outer loop, you only ever need i^j, where i=0..9 and j is the number of digits in the current decade
In fact, you don't need Math.pow() at all, as you can just use multiplication at each decade
Applying my ideas from my comment above and also changing the method signature, you get something that runs about 30 times faster:
public static Long[] getNumbers(long N) {
int porog = 10;
LinkedList<Long> list = new LinkedList<>();
// initial powers for the number 0-9
long[] powers = { 0l, 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l };
for (long i = 1; i < N; i++) {
if (i == porog) {
porog *= 10;
// calculate i^length
for (int pi = 1; pi < 10; pi++) {
powers[pi] *= pi;
}
}
long s = i;
long k = 0;
while (s > 0) {
int r = (int)(s % 10);
k += powers[r];
if (k > i)
break;
s /= 10;
}
if (k == i)
list.add(i);
}
return list.toArray(new Long[]{});
}

From Rosetta Code blog (not my own code)
public static boolean isNarc(long x){
if(x < 0) return false;
String xStr = Long.toString(x);
int m = xStr.length();
long sum = 0;
for(char c : xStr.toCharArray()){
sum += Math.pow(Character.digit(c, 10), m);
}
return sum == x;
}

It's possible to generate Armstrong Numbers quite efficient. For example, all integers can be generated within 10-15 ms.
We may note that for each multi-set of digits, like [1, 1, 2, 4, 5, 7, 7] there is only one sum of powers, which in its turn may either be or be not represented by the digits from set. In the example 1^7 + 1^7 + 2^7 + 4^7 + 5^7 + 7^7 + 7^7 = 1741725, which can be represented by the digits and thus is an Armstrong number.
We may build an algorithm basing on this consideration.
For each number length from 1 to N
Generate all possible multi-sets of N digits
For each multi-set calculate sum of digits^N
Check if it's possible to represent the number we got on step 4 with
the digits from the multi-set
If so - add the number to the result list
The number of cases calculated for each length N is equal to the number of combinations (N + 9, 9) = (N+9)!/(9!N!). Thus for all Ns less than 10 we will generate only 92,377 multi-sets. For N<20: 20,030,009.
Please see GitHub for the description of a few approaches, along with some benchmarking and the Java code. Enjoy! :)

I'm not a professional coder, just self taught with no work experience, so I apologize if my code is bit sloppy.
I took dovetalk's solution and 1) wrote it myself so to better understand it b) made some adjustments that improved the run time considerably for large numbers. I hope this helps anyone else looking for help with this problem:
public static long[] getNumbers(long N) {
long tempII = N;
LinkedHashSet<Long> narcNums = new LinkedHashSet<>();
long tempResult;
long digitLengthTemp = 10;
long tempI;
long[] powers = {0l, 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l};
for (long i = 0; i < N; i++) {
if (i == digitLengthTemp) {
digitLengthTemp *= 10;
for (short x = 2; x < powers.length; x++) powers[x] *= x;
}
//set value of top digits of numbers past first 3 to a remedial value
tempI = i;
long remedialValue = 0;
tempI /= 10; tempI /= 10; tempI /= 10;
while (tempI > 0) {
short index = (short) (tempI % 10);
remedialValue += powers[index];
tempI /= 10;
}
//only passes 1000 at a time to this loop and adds each result to remedial top half
for (int j = 0; j < (tempII > 1000 ? 1000 : tempII); j++) {
//sets digit length and increases the values in array
if (i == 0 && j == digitLengthTemp) {
digitLengthTemp *= 10;
for (short x = 2; x < powers.length; x++) powers[x] *= x;
}
//resets temp results
tempResult = remedialValue;
tempI = j;
//gets the sum of each (digit^numberLength) of number passed to it
while (tempI > 0) {
if (tempResult > i + j) break;
short index = (short) (tempI % 10);
tempResult += powers[index];
tempI /= 10;
}
//checks if sum equals original number
if (i + j == tempResult) narcNums.add(i + j);
}
i += 999; // adds to i in increments of 1000
tempII -= 1000;
}
//converts to long array
long[] results = new long[narcNums.size()];
short i = 0;
for (long x : narcNums) {
results[i++] = x;
}
return results;
}

A major optimisation is to not examine all the numbers in range 1..N . Have a look here.

Related

How to reduce the number of loops if there is no change?

This code is radix sort in Java.
Now I can sort. But I want to reduce its functionality if there is no change in the
array, let it stop the loop and show the value.
Where do I have to fix it? Please guide me, thanks in advance.
public class RadixSort {
void countingSort(int inputArray[], int size, int place) {
//find largest element in input array at 'place'(unit,ten's etc)
int k = ((inputArray[0] / place) % 10);
for (int i = 1; i < size; i++) {
if (k < ((inputArray[i] / place) % 10)) {
k = ((inputArray[i] / place) % 10);
}
}
//initialize the count array of size (k+1) with all elements as 0.
int count[] = new int[k + 1];
for (int i = 0; i <= k; i++) {
count[i] = 0;
}
//Count the occurrence of each element of input array based on place value
//store the count at place value in count array.
for (int i = 0; i < size; i++) {
count[((inputArray[i] / place) % 10)]++;
}
//find cumulative(increased) sum in count array
for (int i = 1; i < (k + 1); i++) {
count[i] += count[i - 1];
}
//Store the elements from input array to output array using count array.
int outputArray[] = new int[size];
for (int j = (size - 1); j >= 0; j--) {
outputArray[count[((inputArray[j] / place) % 10)] - 1] = inputArray[j];
count[(inputArray[j] / place) % 10]--;//decrease count by one.
}
for (int i = 0; i < size; i++) {
inputArray[i] = outputArray[i];//copying output array to input array.
}
System.out.println(Arrays.toString(inputArray));
}
void radixSort(int inputArray[], int size) {
//find max element of inputArray
int max = inputArray[0];
for (int i = 1; i < size; i++) {
if (max < inputArray[i]) {
max = inputArray[i];
}
}
//find number of digits in max element
int d = 0;
while (max > 0) {
d++;
max /= 10;
}
//Use counting cort d no of times
int place = 1;//unit place
for (int i = 0; i < d; i++) {
System.out.print("iteration no = "+(i+1)+" ");
countingSort(inputArray, size, place);
place *= 10;//ten's , hundred's place etc
}
}
1
I'm going to resist typing out some code for you and instead go over the concepts since this looks like homework.
If I'm understanding you correctly, your problem boils down to: "I want to check if two arrays are equivalent and if they are, break out of a loop". Lets tackle the latter part first.
In Java, you can use the keyword"
break;
to break out of a loop.
A guide for checking if two arrays are equivalent in java can be found here:
https://www.geeksforgeeks.org/compare-two-arrays-java/
Sorry if this doesnt answer your question. Im just gonna suggest a faster way to find the digits of each element. Take the log base 10 of the element and add 1.
Like this : int digits = (int) Math.log10(i)+1;

Project Euler #49 Java

The question is -
The arithmetic sequence, 1487, 4817, 8147, in which each of the terms
increases by 3330, is unusual in two ways: (i) each of the three terms
are prime, and, (ii) each of the 4-digit numbers are permutations of
one another.
There are no arithmetic sequences made up of three 1-, 2-, or 3-digit
primes, exhibiting this property, but there is one other 4-digit
increasing sequence.
What 12-digit number do you form by concatenating the three terms in
this sequence?
I've written this code -
package Problems;
import java.util.ArrayList;
import java.util.LinkedList;
public class Pro49 {
private static boolean isPrime(int n){
if(n%2 == 0) return false;
for(int i = 3; i<= Math.sqrt(n); i++){
if(n%i == 0) return false;
}
return true;
}
private static boolean isPerm(int m, int n){
ArrayList<Integer> mArr = new ArrayList<>();
ArrayList<Integer> nArr = new ArrayList<>();
for(int i = 0; i<4; i++){
mArr.add(m%10);
m /= 10;
}
for(int i = 0; i<4; i++){
nArr.add(n%10);
n /= 10;
}
return mArr.containsAll(nArr);
}
public static void main(String[] args) {
LinkedList<Integer> primes = new LinkedList<>();
for(int i = 1001; i<10000; i++){
if(isPrime(i)) primes.add(i);
}
int k = 0;
boolean breaker = false;
for(int i = 0; i<primes.size() - 2; i++){
for(int j = i + 1; j<primes.size() - 1; j++){
if(isPerm(primes.get(i), primes.get(j))) {
k = primes.get(j) + (primes.get(j) - primes.get(i));
if(k<10000 && primes.contains(k) && isPerm(primes.get(i), k)) {
System.out.println(primes.get(i) + "\n" + primes.get(j) + "\n" + k);
breaker = true;
break;
}
}
if(breaker) break;
}
if(breaker) break;
}
}
}
I added the print line System.out.println(primes.get(i) + "\n" + primes.get(j) + "\n" + k); to check the numbers. I got 1049, 1499, 1949 which are wrong. (At least 1049 is wrong I guess).
Can any one point out where my code/logic is wrong?
Any help is appreciated.
I think where your logic is going wrong is your isPerm method. You are using AbstractCollection#containsAll, which, AFAIK, only checks if the parameters are in the collection at least once.
i.e. it basically does
for(E e : collection)
if(!this.contains(e)) return false;
return true;
Therefore, for example, 4999 will be a permutation of 49 because 49 contains 4 and 9 (while it is clearly not based on your example).
The reason why your method seems to work for these values is that you are looping a fixed amount of time - that is, 4. For a number like 49 you will end up with {9, 4, 0, 0} instead of {9, 4}. Do something like this:
while(n != 0) {
nArr.add(n%10);
n /= 10;
}
and you will get the correct digit Lists (and see that containsAll won't work.)
Add the 4-digit restriction elsewhere (e.g. in your loop.)
Maybe you could check the occurrences per digit.
For example:
int[] occurrencesA = new int[10], occurrencesB = new int[10];
for(; m != 0; m /= 10)
occurrencesA[m % 10]++;
for(; n != 0; n /= 10)
occurrencesB[n % 10]++;
for(int i = 0; i < 10; i++)
if(occurrencesA[i] != occurrencesB[i]) return false;
return true;
I found a possible alternative for isPerm
private static boolean isPerm(int m, int n){
ArrayList<Integer> mArr = new ArrayList<>();
ArrayList<Integer> nArr = new ArrayList<>();
final String mS = Integer.toString(m);
final String nS = Integer.toString(n);
if(mS.length() != nS.length()) return false;
for(int i = 0; i<mS.length(); i++){
mArr.add(m%10);
m /= 10;
}
for(int i = 0; i<nS.length(); i++){
nArr.add(n%10);
n /= 10;
}
return (mArr.containsAll(nArr) && nArr.containsAll(mArr));
}
This is giving me the correct answer. Another alternative is posted by some other person below.

Calculate factorial of 50 using array only in java

I'm a total beginner of java.
I have a homework to write a complete program that calculates the factorial of 50 using array.
I can't use any method like biginteger.
I can only use array because my professor wants us to understand the logic behind, I guess...
However, he didn't really teach us the detail of array, so I'm really confused here.
Basically, I'm trying to divide the big number and put it into array slot. So if the first array gets 235, I can divide it and extract the number and put it into one array slot. Then, put the remain next array slot. And repeat the process until I get the result (which is factorial of 50, and it's a huge number..)
I tried to understand what's the logic behind, but I really can't figure it out.. So far I have this on my mind.
import java.util.Scanner;
class Factorial
{
public static void main(String[] args)
{
int n;
Scanner kb = new Scanner(System.in);
System.out.println("Enter n");
n = kb.nextInt();
System.out.println(n +"! = " + fact(n));
}
public static int fact(int n)
{
int product = 1;
int[] a = new int[100];
a[0] = 1;
for (int j = 2; j < a.length; j++)
{
for(; n >= 1; n--)
{
product = product * n;
a[j-1] = n;
a[j] = a[j]/10;
a[j+1] = a[j]%10;
}
}
return product;
}
}
But it doesn't show me the factorial of 50.
it shows me 0 as the result, so apparently, it's not working.
I'm trying to use one method (fact()), but I'm not sure that's the right way to do.
My professor mentioned about using operator / and % to assign the number to the next slot of array repeatedly.
So I'm trying to use that for this homework.
Does anyone have an idea for this homework?
Please help me!
And sorry for the confusing instruction... I'm confused also, so please forgive me.
FYI: factorial of 50 is 30414093201713378043612608166064768844377641568960512000000000000
Try this.
static int[] fact(int n) {
int[] r = new int[100];
r[0] = 1;
for (int i = 1; i <= n; ++i) {
int carry = 0;
for (int j = 0; j < r.length; ++j) {
int x = r[j] * i + carry;
r[j] = x % 10;
carry = x / 10;
}
}
return r;
}
and
int[] result = fact(50);
int i = result.length - 1;
while (i > 0 && result[i] == 0)
--i;
while (i >= 0)
System.out.print(result[i--]);
System.out.println();
// -> 30414093201713378043612608166064768844377641568960512000000000000
Her's my result:
50 factorial - 30414093201713378043612608166064768844377641568960512000000000000
And here's the code. I hard coded an array of 100 digits. When printing, I skip the leading zeroes.
public class FactorialArray {
public static void main(String[] args) {
int n = 50;
System.out.print(n + " factorial - ");
int[] result = factorial(n);
boolean firstDigit = false;
for (int digit : result) {
if (digit > 0) {
firstDigit = true;
}
if (firstDigit) {
System.out.print(digit);
}
}
System.out.println();
}
private static int[] factorial(int n) {
int[] r = new int[100];
r[r.length - 1] = 1;
for (int i = 1; i <= n; i++) {
int carry = 0;
for (int j = r.length - 1; j >= 0; j--) {
int x = r[j] * i + carry;
r[j] = x % 10;
carry = x / 10;
}
}
return r;
}
}
How about:
public static BigInteger p(int numOfAllPerson) {
if (numOfAllPerson < 0) {
throw new IllegalArgumentException();
}
if (numOfAllPerson == 0) {
return BigInteger.ONE;
}
BigInteger retBigInt = BigInteger.ONE;
for (; numOfAllPerson > 0; numOfAllPerson--) {
retBigInt = retBigInt.multiply(BigInteger.valueOf(numOfAllPerson));
}
return retBigInt;
}
Please recall basic level of math how multiplication works?
2344
X 34
= (2344*4)*10^0 + (2344*3)*10^1 = ans
2344
X334
= (2344*4)*10^0 + (2344*3)*10^1 + (2344*3)*10^2= ans
So for m digits X n digits you need n list of string array.
Each time you multiply each digits with m. and store it.
After each step you will append 0,1,2,n-1 trailing zero(s) to that string.
Finally, sum all of n listed string. You know how to do that.
So up to this you know m*n
now it is very easy to compute 1*..........*49*50.
how about:
int[] arrayOfFifty = new int[50];
//populate the array with 1 to 50
for(int i = 1; i < 51; i++){
arrayOfFifty[i-1] = i;
}
//perform the factorial
long result = 1;
for(int i = 0; i < arrayOfFifty.length; i++){
result = arrayOfFifty[i] * result;
}
Did not test this. No idea how big the number is and if it would cause error due to the size of the number.
Updated. arrays use ".length" to measure the size.
I now updated result to long data type and it returns the following - which is obviously incorrect. This is a massive number and I'm not sure what your professor is trying to get at.
-3258495067890909184

Dynamic Programming (Codility Q: NumberSolitaire)

This is the question:
codility.com/programmers/task/number_solitaire
and below link is my result (50% from Codility):
https://codility.com/demo/results/training8AMJZH-RTA/
My code (at the first, I tried to solve this problem using Kadane's Algo):
class Solution {
public int solution(int[] A) {
int temp_max = Integer.MIN_VALUE;
int max = 0;
int k = 1;
if(A.length == 2) return A[0] + A[A.length-1];
for(int i = 1; i < A.length-1; i++) {
if(temp_max < A[i]) temp_max = A[i];
if(A[i] > 0) {
max += A[i];
temp_max = Integer.MIN_VALUE;
k = 0;
} else if(k % 6 == 0) {
max += temp_max;
temp_max = Integer.MIN_VALUE;
k = 0;
}
k++;
}
return A[0] + max + A[A.length-1];
}
And below is the solution (100% from Codility result) that I found from web:
class Solution {
public int solution(int[] A) {
int[] store = new int[A.length];
store[0] = A[0];
for (int i = 1; i < A.length; i++) {
store[i] = store[i-1];
for (int minus = 2; minus <= 6; minus++) {
if (i >= minus) {
store[i] = Math.max(store[i], store[i - minus]);
} else {
break;
}
}
store[i] += A[i];
}
return store[A.length - 1];
}
}
I have no idea what is the problem with my code:(
I tried several test cases but, nothing different with the solution & my code
but, codility test result shows mine is not perfectly correct.
(https://codility.com/demo/results/training8AMJZH-RTA/)
please anyone explain me the problem with my code~~
Try this test case[-1, -2, -3, -4, -3, -8, -5, -2, -3, -4, -5, -6, -1].
you solution return -4 (A[0],A[1],A[length-1],Here is the mistake), but the correct answer is -6 (A[0],A[6],A[length-1]).
Here is a my solution,easy to understand:
public int solution(int[] A) {
int lens = A.length;
int dp[] = new int[6];
for (int i = 0; i < 6; i++) {
dp[i] = A[0];
}
for (int i = 1; i < lens; i++) {
dp[i%6] = getMax6(dp) + A[i];
}
return dp[(lens-1)%6];
}
private int getMax6(int dp[]){
int max = dp[0];
for (int i = 1; i < dp.length; i++) {
max = Math.max(max, dp[i]);
}
return max;
}
Readable solution from Java:
public class Solution {
public static void main(String[] args) {
System.out.println(new Solution().solution(new int[]{1, -2, 0, 9, -1, -2}));
}
private int solution(int[] A) {
int N = A.length;
int[] dp = new int[N];
dp[0] = A[0];
for (int i = 1; i < N; i++) {
double sm = Double.NEGATIVE_INFINITY;
for (int j = 1; j <= 6; j++) {
if (i - j < 0) {
break;
}
double s1 = dp[i - j] + A[i];
sm = Double.max(s1, sm);
}
dp[i] = (int) sm;
}
return dp[N-1];
}
}
Here is a solution similar to #0xAliHn but using less memory. You only need to remember the last 6 moves.
def NumberSolitaire(A):
dp = [0] * 6
dp[-1] = A[0]
for i in range(1, len(A)):
maxVal = -100001
for k in range(1, 7):
if i-k >= 0:
maxVal = max(maxVal, dp[-k] + A[i])
dp.append(maxVal)
dp.pop(0)
return dp[-1]
Based on the solutions posted, I made nice readable code. Not best performance.
int[] mark = new int[A.length];
mark[0] = A[0];
IntStream.range(1, A.length)
.forEach(i -> {
int max = Integer.MIN_VALUE;
mark[i] = IntStream.rangeClosed(1, 6)
.filter(die -> i - die >= 0)
.map(r -> Math.max(mark[i - r] + A[i], max))
.max().orElse(max);
});
return mark[A.length - 1];
Because you are not using dynamic programming, you are using greedy algorithm. Your code will fail when the max number in a range will not be the right choice.
function solution(A) {
// This array contains a maximal value at any index.
const maxTill = [A[0]];
// It's a dynamic programming so we will choose maximal value at each
// Index untill we reach last index (goal)
for (let i = 1; i < A.length; i++) {
// Step 1 . max value of each index will be atleast equal to or greater than
// max value of last index.
maxTill[i] = maxTill[i - 1];
// For each index we are finding the max of last 6 array value
// And storing it into Max value.
for (let dice = 1; dice <= 6; dice++) {
// If array index is itself less than backtrack index
// break as you dont have 6 boxes in your left
if (dice > i) {
break;
} else {
// The most important line .
// Basically checking the max of last 6 boxes using a loop.
maxTill[i] = Math.max(
maxTill[i - dice],
maxTill[i]
);
}
}
// Until this point maxStill contains the maximal value
// to reach to that index.
// To end the game we need to touch that index as well, so
// add the value of the index as well.
maxTill[i] += A[i];
}
return maxTill[A.length - 1];
}
console.log(solution([-1, -2, -3, -4, -3, -8, -5, -2, -3, -4, -5, -6, -1]));
This is my solution. I try to make the code easy to apprehend. It might not save space as much as it can.
private static int solution(int A[])
{
// N // N is an integer within the range [2..100,000];
// A[] // each element of array A is an integer within the range [−10,000..10,000].
int N = A.length;
int[] bestResult = new int[N]; // record the current bestResult
Arrays.fill(bestResult, Integer.MIN_VALUE); // fill in with the smallest integer value
// initialize
bestResult[0] = A[0];
for (int i = 0;i < A.length;i++) {
// calculate six possible results every round
for (int j = i + 1; (j < A.length) && (i < A.length) && j < (i + 1) + 6; j++) {
// compare
int preMaxResult = bestResult[j]; // the max number so far
int nowMaxResult = bestResult[i] + A[j]; // the max number at bestResult[i] + A[j]
bestResult[j] = Math.max(preMaxResult, nowMaxResult);
}
}
return bestResult[bestResult.length-1];
}
Here is the simple Python 3 solution:
import sys
def solution(A):
dp = [0] * len(A)
dp[0] = A[0]
for i in range(1, len(A)):
maxVal = -sys.maxsize - 1
for k in range(1, 7):
if i-k >= 0:
maxVal = max(maxVal, dp[i-k] + A[i])
dp[i] = maxVal
return dp[len(A)-1]
100% c++ solution(
results)
#include <climits>
int solution(vector<int>& A) {
const int N = A.size();
if (N == 2)
return A[0] + A[1];
vector<int> MaxSum(N, INT_MIN);
MaxSum[0] = A[0];
for (int i = 1; i < N; i++) {
for (int dice = 1; dice <= 6; dice++) {
if (dice > i)
break;
MaxSum[i] = max(MaxSum[i], A[i] + MaxSum[i - dice]);
}
}
return MaxSum[N-1];
}
100% python solution
with the help of the answers above and https://sapy.medium.com/cracking-the-coding-interview-30eb419c4c57
def solution(A):
# write your code in Python 3.6
# initialize maxUntil [0]*n
n = len(A)
maxUntil = [0 for i in range(n)]
maxUntil[0]=A[0]
# fill in maxUntil, remember to chack limits
for i in range(1, n): # for each
maxUntil[i] = maxUntil [i-1]
# check the max 6 to the left:
# for 1,..,6:
for dice in range(1,7):
if dice > i: # if dice bigger than loc - we are out of range
break
#else: check if bigger than cur elem, if so - update elem
maxUntil[i] = max(maxUntil[i],maxUntil[i-dice])
# add the current jump:
maxUntil[i] +=A[i]
# must reach the last sq:
return maxUntil[n-1]
I would like to explain the algorithm I have come up with and then show you the implementation in C++.
Create a hash for the max sums. We only need to store the elements within reach, so the last 6 elements. This is because the dice can only go back so much.
Initialise the hash with the first element in the array for simplicity since the first element in this hash equals to the first element of the inputs.
Then go through the input elements from the second element.
For each iteration, find the maximum values from the last 6 indices. Add the current value to that to get the current max sum.
When we reach the end of the inputs, exit the loop.
Return the max sum of the last element calculated. For this, we need clipping with module due to the space optimisation
The runtime complexity of this dynamic programming solution is O(N) since we go through element in the inputs. If we consider the dice range K, then this would be O(N * K).
The space complexity is O(1) because we have a hash for the last six elements. It is O(K) if we does not consider the number of dice faces constant, but K.
int solution(vector<int> &A)
{
vector<int> max_sums(6, A[0]);
for (size_t i = 1; i < A.size(); ++i) max_sums[i % max_sums.size()] = *max_element(max_sums.cbegin(), max_sums.cend()) + A[i];
return max_sums[(A.size() - 1) % max_sums.size()];
}
Here's my answer which gives 100% for Kotlin
val pr = IntArray(A.size) { Int.MIN_VALUE }
pr[0] = A.first()
for ((index, value) in pr.withIndex()) {
for (i in index + 1..min(index + 6, A.lastIndex)) {
pr[i] = max(value + A[i], pr[i])
}
}
return pr.last()
I used forwarded prediction, where I fill the next 6 items of the max value the current index can give.

What part of my code is making my performance suffer? (Codility's MaxCounter)

I have the following problem:
You are given N counters, initially set to 0, and you have two possible operations on them:
increase(X) − counter X is increased by 1,
max counter − all counters are set to the maximum value of any counter.
A non-empty zero-indexed array A of M integers is given. This array represents consecutive operations:
if A[K] = X, such that 1 ≤ X ≤ N, then operation K is increase(X),
if A[K] = N + 1 then operation K is max counter.
For example, given integer N = 5 and array A such that:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the values of the counters after each consecutive operation will be:
(0, 0, 1, 0, 0)
(0, 0, 1, 1, 0)
(0, 0, 1, 2, 0)
(2, 2, 2, 2, 2)
(3, 2, 2, 2, 2)
(3, 2, 2, 3, 2)
(3, 2, 2, 4, 2)
The goal is to calculate the value of every counter after all operations.
Write a function:
class Solution { public int[] solution(int N, int[] A); }
that, given an integer N and a non-empty zero-indexed array A consisting of M integers, returns a sequence of integers representing the values of the counters.
For example, given:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the function should return [3, 2, 2, 4, 2], as explained above.
Assume that:
N and M are integers within the range [1..100,000];
each element of array A is an integer within the range [1..N + 1].
Complexity:
expected worst-case time complexity is O(N+M);
expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
I have answered this problem using the following code, but only got 80% as opposed to 100% performance, despite having O(N+M) time complexity:
public class Solution {
public int[] solution(int N, int[] A) {
int highestCounter = N;
int minimumValue = 0;
int lastMinimumValue = 0;
int [] answer = new int[N];
for (int i = 0; i < A.length; i++) {
int currentCounter = A[i];
int answerEquivalent = currentCounter -1;
if(currentCounter >0 && currentCounter<=highestCounter){
answer[answerEquivalent] = answer[answerEquivalent]+1;
if(answer[answerEquivalent] > minimumValue){
minimumValue = answer[answerEquivalent];
}
}
if (currentCounter == highestCounter +1 && lastMinimumValue!=minimumValue){
lastMinimumValue = minimumValue;
Arrays.fill(answer, minimumValue);
}
}
return answer;
}
}
Where is my performance here suffering? The code gives the right answer, but does not perform up-to-spec despite having the right time complexity.
Instead of calling Arrays.fill(answer, minimumValue); whenever you encounter a "max counter" operation, which takes O(N), you should keep track of the last max value that was assigned due to "max counter" operation, and update the entire array just one time, after all the operations are processed. This would take O(N+M).
I changed the variables names from min to max to make it less confusing.
public class Solution {
public int[] solution(int N, int[] A) {
int highestCounter = N;
int maxValue = 0;
int lastMaxValue = 0;
int [] answer = new int[N];
for (int i = 0; i < A.length; i++) {
int currentCounter = A[i];
int answerEquivalent = currentCounter -1;
if(currentCounter >0 && currentCounter<=highestCounter){
if (answer[answerEquivalent] < lastMaxValue)
answer[answerEquivalent] = lastMaxValue +1;
else
answer[answerEquivalent] = answer[answerEquivalent]+1;
if(answer[answerEquivalent] > maxValue){
maxValue = answer[answerEquivalent];
}
}
if (currentCounter == highestCounter +1){
lastMaxValue = maxValue;
}
}
// update all the counters smaller than lastMaxValue
for (int i = 0; i < answer.length; i++) {
if (answer[i] < lastMaxValue)
answer[i] = lastMaxValue;
}
return answer;
}
}
The following operation is O(n) time:
Arrays.fill(answer, minimumValue);
Now, if you are given a test case where the max counter operation is repeated often (say n/3 of the total operations) - you got yourself an O(n*m) algorithm (worst case analysis), and NOT O(n+m).
You can optimize it to be done in O(n+m) time, by using an algorithm that initializes an array in O(1) every time this operation happens.
This will reduce worst case time complexity from O(n*m) to O(n+m)1
(1)Theoretically, using the same idea, it can even be done in O(m) - regardless of the size of the number of counters, but the first allocation of the arrays takes O(n) time in java
This is a bit like #Eran's solution but encapsulates the functionality in an object. Essentially - keep track of a max value and an atLeast value and let the object's functionality do the rest.
private static class MaxCounter {
// Current set of values.
final int[] a;
// Keeps track of the current max value.
int currentMax = 0;
// Min value. If a[i] < atLeast the a[i] should appear as atLeast.
int atLeast = 0;
public MaxCounter(int n) {
this.a = new int[n];
}
// Perform the defined op.
public void op(int k) {
// Values are one-based.
k -= 1;
if (k < a.length) {
// Increment.
inc(k);
} else {
// Set max
max(k);
}
}
// Increment.
private void inc(int k) {
// Get new value.
int v = get(k) + 1;
// Keep track of current max.
if (v > currentMax) {
currentMax = v;
}
// Set new value.
a[k] = v;
}
private int get(int k) {
// Returns eithe a[k] or atLeast.
int v = a[k];
return v < atLeast ? atLeast : v;
}
private void max(int k) {
// Record new max.
atLeast = currentMax;
}
public int[] solution() {
// Give them the solution.
int[] solution = new int[a.length];
for (int i = 0; i < a.length; i++) {
solution[i] = get(i);
}
return solution;
}
#Override
public String toString() {
StringBuilder s = new StringBuilder("[");
for (int i = 0; i < a.length; i++) {
s.append(get(i));
if (i < a.length - 1) {
s.append(",");
}
}
return s.append("]").toString();
}
}
public void test() {
System.out.println("Hello");
int[] p = new int[]{3, 4, 4, 6, 1, 4, 4};
MaxCounter mc = new MaxCounter(5);
for (int i = 0; i < p.length; i++) {
mc.op(p[i]);
System.out.println(mc);
}
int[] mine = mc.solution();
System.out.println("Solution = " + Arrays.toString(mine));
}
My solution: 100\100
class Solution
{
public int maxCounterValue;
public int[] Counters;
public void Increase(int position)
{
position = position - 1;
Counters[position]++;
if (Counters[position] > maxCounterValue)
maxCounterValue = Counters[position];
}
public void SetMaxCounter()
{
for (int i = 0; i < Counters.Length; i++)
{
Counters[i] = maxCounterValue;
}
}
public int[] solution(int N, int[] A)
{
if (N < 1 || N > 100000) return null;
if (A.Length < 1) return null;
int nlusOne = N + 1;
Counters = new int[N];
int x;
for (int i = 0; i < A.Length; i++)
{
x = A[i];
if (x > 0 && x <= N)
{
Increase(x);
}
if (x == nlusOne && maxCounterValue > 0) // this used for all maxCounter values in array. Reduces addition loops
SetMaxCounter();
if (x > nlusOne)
return null;
}
return Counters;
}
}
( #molbdnilo : +1 !) As this is just an algorithm test, there's no sense getting too wordy about variables. "answerEquivalent" for a zero-based array index adjustment? Gimme a break ! Just answer[A[i] - 1] will do.
Test says to assume A values always lie between 1 and N+1. So checking for this is not needed.
fillArray(.) is an O(N) process which is within an O(M) process. This makes the whole code into an O(M*N) process when the max complexity desired is O(M+N).
The only way to achieve this is to only carry forward the current max value of the counters. This allows you to always save the correct max counter value when A[i] is N+1. The latter value is a sort of baseline value for all increments afterwards. After all A values are actioned, those counters which were never incremented via array entries can then be brought up to the all-counters baseline via a second for loop of complexity O(N).
Look at Eran's solution.
This is how we can eliminate O(N*M) complexity.
In this solutions, instead of populating result array for every A[K]=N+1, I tried to keep what is min value of all elements, and update result array once all operation has been completed.
If there is increase operation then updating that position :
if (counter[x - 1] < minVal) {
counter[x - 1] = minVal + 1;
} else {
counter[x - 1]++;
}
And keep track of minVal for each element of result array.
Here is complete solution:
public int[] solution(int N, int[] A) {
int minVal = -1;
int maxCount = -1;
int[] counter = new int[N];
for (int i = 0; i < A.length; i++) {
int x = A[i];
if (x > 0 && x <= N) {
if (counter[x - 1] < minVal) {
counter[x - 1] = minVal + 1;
} else {
counter[x - 1]++;
}
if (maxCount < counter[x - 1]) {
maxCount = counter[x - 1];
}
}
if (x == N + 1 && maxCount > 0) {
minVal = maxCount;
}
}
for (int i = 0; i < counter.length; i++) {
if (counter[i] < minVal) {
counter[i] = minVal;
}
}
return counter;
}
This is my swift 3 solution (100/100)
public func solution(_ N : Int, _ A : inout [Int]) -> [Int] {
var counters = Array(repeating: 0, count: N)
var _max = 0
var _min = 0
for i in A {
if counters.count >= i {
let temp = max(counters[i-1] + 1, _min + 1)
_max = max(temp, _max)
counters[i-1] = temp
} else {
_min = _max
}
}
return counters.map { max($0, _min) }
}

Categories