patterncppMinor
Find the Sum of Digit XOR till N?
Viewed 0 times
xorfindthesumdigittill
Problem
The question is to find the sum of Digit XOR till \$N\$.
For eg:-
Digit XOR of a number 112
112 => 1 xor 1 xor 2 = 2
So we need to find
My Solution Approach was as follows,
But this is \$O(nlogn)\$, which is time expensive.
Then I found a better solution like the below, which takes \$O(n)\$:
But this too is time expensive for larger \$N\$.
Is there any direct formula to calculate this SUM in \$O(1)\$.
For eg:-
Digit XOR of a number 112
112 => 1 xor 1 xor 2 = 2
So we need to find
int digit_xor_sum=0
for(i=1;i<=N;i++){
digit_xor_sum+= digit_xor(i);
}
return digit_xor_sumMy Solution Approach was as follows,
int digit_xor(int n){
if(n<=9){
return n; // because there is only 1 digit
}
int digit_xor_sol=(n%10); // intialising digit_xor_sol with last digit
n/=10; // removing the last digit
while(n){
digit_xor_sol = digit_xor_sol ^ (n%10); // xor the current last digit and current xor solution
n/=10;
}
return digit_xor_sol;
}But this is \$O(nlogn)\$, which is time expensive.
Then I found a better solution like the below, which takes \$O(n)\$:
int digit_xor[1000];
int find_digit_xor(int n){
if(n<=9)
return digit_xor[n]=n;
if(digit_xor[n]!=-1){ // there are multiple test-cases, so no need to recompute again
return digit_xor[n];
}
return digit_xor[n] = digit_xor[n/10]^(n%10);
}
int main(){
int sum;
memset(digit_xor,-1,sizeof digit_xor);
int testcase;
scanf("%d",&testcase);
while(testcase--){
int n;
scanf("%d",&n);
sum=0;
for(int i=0;i<=n;i++)
sum+=find_digit_xor(i);
printf("%d\n",sum);
}
return 0;
}But this too is time expensive for larger \$N\$.
Is there any direct formula to calculate this SUM in \$O(1)\$.
Solution
Updated review
Peter Taylor noticed that I was solving for the wrong problem. The problem calls for a sum of xor sums, not a single xor sum. So I'm updating a review to talk about the actual problem (the old review is at the end, for reference).
Current solution is \$O(n)\$, is it enough?
The current solution with memoization runs in \$O(n)\$ time. I was able to run with
Can be solved in \$O(\log n)\$ time and space
Peter Taylor's answer outlined a solution where you solve for each of four bits (1,2,4,8) separately and then add up to a final total. This solution requires \$O(\log n)\$ time due to its recursive nature, but does not use any extra space other than the \$O(\log n)\$ space needed to handle the recursion.
I got interested in how difficult it would be to implement this solution, so I did it. I also tested versus the memoized version to make sure it worked. Here is the result (quite long due to the number of comments):
Old review (solves a different problem)
I don't know about \$O(1)\$ but it seems doable in \$O(\log n)\$. Given a number \$n\$, you can compute how many times each digit appears in each decimal place. For example, given the number 1000, I can tell you the digit 1 appears 1 time in the thousands digit, 100 times in the hundreds digit, 100 times in the tens digit, and 100 times in the ones digit, for a total of 301 times.
If you
Now the question is, how did I compute the count of the digit 1 in each decimal place? I suggest you start with the ones place (the rightmost digit) and think about why the answer for any digit is always
Then your main computation would look like:
I'll leave it up to the reader to figure out how to write
Peter Taylor noticed that I was solving for the wrong problem. The problem calls for a sum of xor sums, not a single xor sum. So I'm updating a review to talk about the actual problem (the old review is at the end, for reference).
Current solution is \$O(n)\$, is it enough?
The current solution with memoization runs in \$O(n)\$ time. I was able to run with
n = 200000000 (200 million) in under one second. The question did not specify how many test cases there would be and how large \$n\$ could be for each testcase. But since the OP said it wasn't fast enough, I'm going to assume that \$n\$ could be very large. In that case, both time and space become a concern.Can be solved in \$O(\log n)\$ time and space
Peter Taylor's answer outlined a solution where you solve for each of four bits (1,2,4,8) separately and then add up to a final total. This solution requires \$O(\log n)\$ time due to its recursive nature, but does not use any extra space other than the \$O(\log n)\$ space needed to handle the recursion.
I got interested in how difficult it would be to implement this solution, so I did it. I also tested versus the memoized version to make sure it worked. Here is the result (quite long due to the number of comments):
#include
#include
#include
#include
#define MAX_DIGITS 20
const uint8_t ones[] = { 1, 3, 5, 7, 9 };
const uint8_t twos[] = { 2, 3, 6, 7 };
const uint8_t fours[] = { 4, 5, 6, 7 };
const uint8_t eights[] = { 8, 9 };
// Returns true if "digit" is in the given set of digits "digitSet".
// For example, 3 is in the set { 1, 3, 5, 7, 9 }.
static bool isInSet(int digit, const uint8_t *digitSet, int numDigitSet)
{
int i = 0;
for (i=0;i 0) {
digits[numDigits++] = n % 10;
n /= 10;
}
sum += computeXorSumForBit(digits, numDigits, ones, sizeof(ones), true);
sum += 2*computeXorSumForBit(digits, numDigits, twos, sizeof(twos), true);
sum += 4*computeXorSumForBit(digits, numDigits, fours, sizeof(fours), true);
sum += 8*computeXorSumForBit(digits, numDigits, eights,sizeof(eights),true);
return sum;
}
int main(void)
{
uint64_t sum = 0;
int numTests = 0;
scanf("%d", &numTests);
while (numTests--) {
uint64_t n;
scanf("%lld", &n);
sum += computeXorSum(n);
}
printf("%lld\n", sum);
return 0;
}Old review (solves a different problem)
I don't know about \$O(1)\$ but it seems doable in \$O(\log n)\$. Given a number \$n\$, you can compute how many times each digit appears in each decimal place. For example, given the number 1000, I can tell you the digit 1 appears 1 time in the thousands digit, 100 times in the hundreds digit, 100 times in the tens digit, and 100 times in the ones digit, for a total of 301 times.
If you
xor a digit an even number of times, you get 0. If you xor a digit an odd number of times, you get the digit. So in my example, you xor the digit 1 an odd number of times (301 times) and get 1. Then you solve for digits 2..9 and xor all the digit results together.Now the question is, how did I compute the count of the digit 1 in each decimal place? I suggest you start with the ones place (the rightmost digit) and think about why the answer for any digit is always
N/10 or N/10 + 1. Then think about the tens place and think about why the answer is always between N/10 - (digit-1) and N/10 + (10-digit). Once you figure out the pattern, you should be able to write a function like this:int GetCount(int n, int digit, int decimalPlace);Then your main computation would look like:
int ComputeDigitXor(int n)
{
int totalXor = 0;
int maxDecimalPlace = ComputeMaxDecimalPlace(n);
for (int digit = 1; digit <= 9; digit++) {
int count = 0;
for (int place = 0; place <= maxDecimalPlace; place++) {
count += GetCount(n, digit, place);
}
if (count & 1)
totalXor ^= digit;
}
return totalXor;
}I'll leave it up to the reader to figure out how to write
GetCount() given the hints above.Code Snippets
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#define MAX_DIGITS 20
const uint8_t ones[] = { 1, 3, 5, 7, 9 };
const uint8_t twos[] = { 2, 3, 6, 7 };
const uint8_t fours[] = { 4, 5, 6, 7 };
const uint8_t eights[] = { 8, 9 };
// Returns true if "digit" is in the given set of digits "digitSet".
// For example, 3 is in the set { 1, 3, 5, 7, 9 }.
static bool isInSet(int digit, const uint8_t *digitSet, int numDigitSet)
{
int i = 0;
for (i=0;i<numDigitSet;i++) {
if (digitSet[i] == digit)
return 1;
}
return 0;
}
// Returns (n choose k), which is the number of ways to choose k objects from
// a set of n objects.
static uint64_t combinations(int n, int k)
{
int i = 0;
uint64_t ret = 1;
// nCk equals nC(n-k), so solve for the easier case.
if (n-k < k)
k = n-k;
for (i=0;i<k;i++) {
ret *= (n-i);
ret /= (i+1);
}
return ret;
}
// Given every possible number of numDigits length, return the number of ways
// that you can make a number with an odd/even number of digits coming from
// a set of size numInSet. For example:
//
// 5 digits
// 4 numbers in the set
// odd
//
// Let D be a digit in the set and x be a digit not in the set.
//
// Since we are considering only odd possibilities, we can make numbers with
// either 1 D, 3 Ds, or 5 Ds, like this:
//
// Dxxxx xDxxx xxDxx xxxDx xxxxD = 1 D (5 choose 1 = 5 ways to place)
// DDDxx DDxDx DDxxD DxDDx etc. = 3 Ds (5 choose 3 = 10 ways to place)
// DDDDD = 5 Ds (5 choose 5 = 1 way to place)
//
// Within each of the ways above, such as Dxxxx, there are also many ways
// to build the digits. There are numInSet digits (4 in the example) that
// can be put in each D slot, and (10 - numInSet) digits (6 in the example)
// ways to fill each x slot. So for Dxxxx, there are 4*6*6*6*6 ways to create
// a matching number of that pattern. For DDDxx, there are 4*4*4*6*6 ways.
// For DDDDD there are 4*4*4*4*4 ways.
//
// So the answer for the example above would be:
//
// 5*(4*6*6*6*6) + 10*(4*4*4*6*6) + 1*(4*4*4*4*4) = 49984 ways
//
static uint64_t computeCount(int numDigits, int numInSet, bool odd)
{
int i = (odd ? 1 : 0);
int j = 0;
uint64_t ret = 0;
int numNotInSet = 10 - numInSet;
uint64_t waysPerCombo = 1;
// Special case for 0 digits.
if (numDigits == 0)
return odd ? 0 : 1;
// Preinitialize waysPerCombo so we don't have to recompute the whole
// thing every time through the next loop. We will only need to adjust
// it for 2 digits every loop iteration. This is the 4*6*6*6*6 factor
// from the example above.
for (j=0;j<i;j++)
waysPerCombo *= numInSet;
for (j=i;j<numDigits;j++)
waysPerCombo *= numNotInSet;
// Loop through each possible way to make an odd/even number of digits
// up to numDigits (such as 1, 3, 5 in tint GetCount(int n, int digit, int decimalPlace);int ComputeDigitXor(int n)
{
int totalXor = 0;
int maxDecimalPlace = ComputeMaxDecimalPlace(n);
for (int digit = 1; digit <= 9; digit++) {
int count = 0;
for (int place = 0; place <= maxDecimalPlace; place++) {
count += GetCount(n, digit, place);
}
if (count & 1)
totalXor ^= digit;
}
return totalXor;
}Context
StackExchange Code Review Q#150473, answer score: 4
Revisions (0)
No revisions yet.