6

Let's say I want to check if a number n = 123 has duplicate digits. I tried:

#include <iostream>

using namespace std;

int main() {
    int n = 123;
    int d1 = n % 10;
    int d2 = ( n / 10 ) % 10;
    int d3 = ( n / 100 ) % 10;
    if( d1 != d2 && d1 != d3 && d2 != d3 ) {
        cout << n << " does not have duplicate digits.\n";
    }
}

Is there any faster solution to this problem?

Update
Sorry for being unclear. The code above was written in C++ only for description purpose. I have to solve this problem in TI-89, with a number of 9 digits. And since the limitation of memory and speed, I'm looking for a fastest way possible.

TI-89 only has several condition keyword:

  • If
  • If ... Then
  • when(
  • For ... EndFor
  • While ... EndWhile
  • Loop ... EndLoop
  • Custom ... EndCustom

Thanks,
Chan

roxrook
  • 13,511
  • 40
  • 107
  • 156
  • Since your solution is limited to three digit numbers, just make a hash table of the numbers that have repeating digits and check if the number is contained in it. – aaronasterling Jan 26 '11 at 04:54
  • You also need to handle numbers with less than three digits (if that is valid input). Right now `n = 1` will be rejected as having duplicate digits (the leading zeros). – Thilo Jan 26 '11 at 05:07
  • Which language on the TI-89 are you working on? – Dr. belisarius Jan 26 '11 at 05:11
  • @belisarius: It's not really a language. It's designed to solve Math problem only. So it does not have array. Only `for`, `if-else`, `do`, `while` and arithmetic. – roxrook Jan 26 '11 at 05:13
  • @Chan From Wikipedia _For many applications, it is the most convenient way to program any TI calculator, since the capability to write programs in TI-BASIC is built-in. Assembly language (often referred to as "asm") can also be used, and C compilers exist for translation into assembly: TIGCC for Motorola 68000 based calculators, and Z88DK for Zilog Z80 based calculators_ – Dr. belisarius Jan 26 '11 at 05:14
  • @belisarius: wow, I did not know that I can use C. But from the list of built-in keywords, it does not look like C function. – roxrook Jan 26 '11 at 05:18
  • @Chan you'll get many good answers that cannot be mapped to your language if you don't specify one – Dr. belisarius Jan 26 '11 at 05:19
  • @Chan You can use assembler too. With assembler the problem is just convert to string and then a loop with a shift and compare inside it. – Dr. belisarius Jan 26 '11 at 05:21
  • 1
    @Chan Check this for languages recommendations on the TI-89 http://www.ocf.berkeley.edu/~pad/faq/prog.html – Dr. belisarius Jan 26 '11 at 05:26

3 Answers3

11

Not necessarily faster but you should measure anyway, just in case - my optimisation mantra is "measure, don't guess".

But I believe it's clearer in intent (and simple enough to be translated to a simpler calculator language. It's also able to handle arbitrarily sized integers.

int hasDupes (unsigned int n) {
    // Flag to indicate digit has been used, all zero to start.
    int used[10] = {0};

    // More than 10 digits must have duplicates, return true quickly.
    if (n > 9999999999) return 1;

    // Process each digit in number.
    while (n != 0) {
        // If duplicate, return true as soon as found.
        if (used[n%10]) return 1;

        // Otherwise, mark used, go to next digit.
        used[n%10] = 1;
        n /= 10;
    }

    // No duplicates after checking all digits, return false.
    return 0;
}

If you have a limited range of possibilities, you can use the time-honoured approach of sacrificing space for time. For example, let's say you're talking about numbers between 0 and 999 inclusive (the : : markers simply indicate data I've removed to keep the size of the answer manageable):

const int *hasDupes = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //   0 -   9
        0, 1, 0, 0, 0, 0, 0, 0, 0, 0,  //  10 -  19
        0, 0, 1, 0, 0, 0, 0, 0, 0, 0,  //  20 -  29
                    :  :
        0, 0, 1, 0, 0, 1, 0, 0, 0, 0,  // 520 - 529
                    :  :
        0, 1, 0, 0, 0, 0, 0, 0, 1, 0,  // 810 - 819
                    :  :
        0, 0, 0, 0, 0, 0, 0, 1, 0, 1,  // 970 - 979
        0, 0, 0, 0, 0, 0, 0, 0, 1, 1,  // 980 - 989
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 990 - 999
};

and just do a table lookup of hasDupes[n]. The table itself could be generated (once) programmatically and then just inserted into your code for usage.

However, based on your edit where you state you need to handle nine-digit numbers, a billion-element array is probably not going to be possible on your calculator. I would therefore opt for the first solution.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Thanks for your solution. However I just use C++ to describe the problem. I have to program it in TI-89, so I'm looking for a faster way. – roxrook Jan 26 '11 at 04:55
  • @Chan, this version has the advantage of exiting early as soon as a duplicate is found. It would be worth profiling to see. It also has the advantage of working for numbers with any amount of digits (although for it to be optimal in that case, it should just return false as soon as there are more than ten digits: pidgeons and holes) – aaronasterling Jan 26 '11 at 04:58
3
template<class T, int radix = 10>
bool has_duplicate_digits(T n) {
    int digits_mask = 0;
    while (digits_mask |= (1 << (n % radix)), n /= radix)
        if (digits_mask & (1 << (n % radix)))
            return true;
    return false;
}

Something like that should work as long as n is nonnegative and int has at least radix bits.


digits_mask is a bitset (bit 0 represents the occurrence of a 0 digit, bit 1 represents the occurrence of a 1 digit, etc.).

The bitmap is populated with the least significant digit of n, and the rest of the digits are shifted down. If there are more digits, and the new least significant digit is marked as having occurred previously, return true, otherwise repeat.

When there are no more digits, return false.

1 << x returns 1, 2, 4, 8, etc.: masks to use to test/set bits in the bitset.

a |= z is shorthand for a = a | z, which sets bits by the union of a from z.

a & z is the intersection of the bits in a and z, and is zero (false) if none are set and non-zero (true) if any are set.

ephemient
  • 198,619
  • 38
  • 280
  • 391
1

I did a crash course in TI-89 basic to answer :)

Let's see if this works (I haven't an emulator, so can't check).

Test()
Prgm
{0,0,0,0,0,0,0,0,0,0}->A
Title "Request"
Request "Enter a number",B
EndDlog
Expr(B)->B
While  B > 1
 MOD(10,B)->C
 if A[C+1] = 1 goto K 
 1->A[C+1]
 B-C->B 
EndWhile
Title "Done"
Text "Numbers non repeating"
Enddlog
goto J

Lbl K
Title "Done"
Text "Numbers repeating"
Enddlog

Lbl J
EndPrgm
Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190
  • I have no idea if this is correct, but +1 for use of TI-basic :p `B - C -> B` looks fishy though. –  Jan 26 '11 at 06:16
  • @pst I agree, but I learned by example. See the first block of code examples here http://en.wikipedia.org/wiki/TI-BASIC :) – Dr. belisarius Jan 26 '11 at 06:24
  • I mean I'd expected `B / 10 -> B` or similar for the digit lurch. –  Jan 26 '11 at 08:32
  • @pst That needs more knowledge of the language arithmetic than I have! How do you know it will return an integer? – Dr. belisarius Jan 26 '11 at 09:33