Computing using Arrays

Hi guys,

I wish to know, how I can use each of the digits of a 15 digit code supplied by a user to compute a formula like this:
Given that the 15 digit code is represented as ABCDEFGHIJKLMNO
A*3+B*7+C*3+D*3+E*7F*3+G*3+H*7+I*3+J*3+K*7+L*3+M*3+N*7+O*3

How do i find the result of this computation using an array to represent the 15 digit code in which user will input sigle digits from A to O.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <string>
#include <cctype> // std::isdigit
#include <algorithm> // std::all_of
#include <iterator> // std::size
#include <numeric> // std::inner_product

constexpr std::size_t NDIGITS = 15 ;

bool is_valid( const std::string& str )
{
    // valid if str has exactly NDIGITS characters
    // and every character is a decimal digit
    // https://en.cppreference.com/w/cpp/algorithm/all_any_none_of
    // https://en.cppreference.com/w/cpp/string/byte/isdigit
    // http://www.stroustrup.com/C++11FAQ.html#lambda
    return str.size() == NDIGITS &&
           std::all_of( str.begin(), str.end(),
                        []( unsigned char c ) { return std::isdigit(c) ; } ) ;
}

int computed_checksum( std::string str )
{
    if( !is_valid(str) ) return -1 ; // invalid string, give up

    static constexpr int multipliers[] { 3, 7, 3, 3, 7, 3, 3, 7, 3, 3, 7, 3, 3, 7, 3 };
    // http://www.stroustrup.com/C++11FAQ.html#static_assert
    // https://en.cppreference.com/w/cpp/iterator/size
    static_assert( std::size(multipliers) == NDIGITS ) ; // sanity check

    for( char& c : str ) c -= '0' ; // get the integer values of the decimal digit characters
                                    // '3' - '0' == 3, '7' - '0' == 7 etc.

    // https://en.cppreference.com/w/cpp/algorithm/inner_product
    return std::inner_product( str.begin(), str.end(), multipliers, 0 ) ;
}

int main()
{
    std::string digits ; // string (array) to hold the digit characters
    std::cout << "enter a " << NDIGITS << " digit decimal number: " ;
    std::cin >> digits ; // read the input into the string

    if( is_valid(digits) ) std::cout << "checksum: " << computed_checksum(digits) << '\n' ;
    else std::cerr << "invalid input\n" ;
}
Hello CollinsLainzo,

Why do you need an array?

Andy
Thanks @JLBorges for this solution Still a bit complicated for me though. I don't know if someone can provide a simpler solution for a beginner like me lol! @HandyAndy. Sorry Array may nt actually be necessary. Thanks!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

int computed_checksum( unsigned long long number )
{
    // we process three digits at a time

    if( number < 100 ) return 0 ; // less than three digits, nothing more to be done

    // get the right most three digits
    const int r1 = number%10 ; // rightmost digit
    const int r2 = (number/10) % 10 ; // next digit from right to left
    const int r3 = (number/100) % 10 ; // third digit from right to left
   
    // corrected. see post by dutch below 
    return r1*3 + r2*7 + r3*3 + // multiply the digits by 3, 7 and 3 from right to left
           computed_checksum( number/1000 ) ; // and add the checksum for the digits remaining
                                              // after the rightmost three digits are removed
}

int main()
{
    // unsigned long long: guaranteed by the C++ standard to be able to represent
    //                     integers having fifteen decimal digits
    // see: https://en.cppreference.com/w/cpp/language/types#Range_of_values
    unsigned long long number ;
    std::cout << "enter a 15 digit decimal number: " ;
    std::cin >> number ;

    // if it is a 15 digit number
    if( number > 99'999'999'999'999 && number < 1'000'000'000'000'000 )
        std::cout << computed_checksum(number) << '\n' ;

    else std::cerr << "input error: this is not a 15 digit number\n" ;
} 
Last edited on
@JLBorges, it could also be mentioned that std::cin >> number is guaranteed to store 0 in number if it fails (input is non-numeric, negative, too large). So the following test still works properly in that case, too.

And shouldn't the pattern be 3, 7, 3 (not 3, 3, 7)? It also might be reasonable to continue even if there are less than 3 digits. Missing digits would be 0's.
Last edited on
@CollinsLainzo,

I empathize with beginners. What @JLBorges presented is a modern C++ implementation, but it isn't actually complicated. There can be more familiar ways of doing this to beginners, but what you'll learn as you progress forward is that code like that presented by @JLBorges is actually more compact and less complicated than using simpler approaches which actually require more code.

To be clear, it is likely best to use arrays or some form of container like them (vectors come to mind).

@JLBorges took the interesting step of including links to documentation. There's a message for all students in this choice, and it's important. You are going to have to become accustomed to referencing the documentation on the language features, in any language, for as a long as write software. I've done this for over 40 years, and largely because of the evolution of these languages, and because new languages emerge, I may have at any one moment over 20 tabs in browsers on pages of the documentation. Especially when a new version is released (which is every 2 years now with C++).

That said, a walk through and interpretation of what's shown would likely suffice for what you require.

Note the NDIGITS = 15. This is constexpr, which is modern C++, but you could use

const unsigned int NDIGITS = 15;

It would have a similar effect, and would work on older compilers. It establishes a constant value defining the limit of the number of digits you've prescribed.

Skip is_valid for a moment, I'll get back to it.

Move to main.

Main should start out familiar. The user can enter a string of digits.

@JLBorges uses is_valid to be sure the digits string is valid for the computation. One thing not shown here is how to loop to ask again if the input is invalid, and the option of an exit. That's a detail you can easily skip for many student programs, unless, of course, that is required for the assignment (and I see assignments often do).

So, let's focus on the meat of the work, the computed_checksum function. That's what you've requested, a type of checksum. The very formula you show (all those 3's and 7's) is a simple kind of checksum.

You'll notice the "is_valid" is used again here, and that may be redundant.

Now we come to the multipliers array. This is your list of 3's and 7's. In what is the modern style, it is declared where it is to be used (the subjects are related, so this is a good idea). It is declared constexpr, which is modern C++, but you may be more familiar with declaring this as a global int array, outside of a function. This works similarly (and you can exclude the constexpr if it is confusing for now).

This is the only array of integers used. The other 'array' is the string provided by the user. That comes up soon.

If you aren't sure about static_assert, for a student program you may skip this line. It simply tests at compile time that the length of the multipliers array is as expected. You may benefit from reading the documentation at those links.

Now, the for loop. This is the modern (C++11) means of looping through a standard container. 'str', being a string, is looped from beginning to end (automatically checking the end of the string in this form).

for( char &c : str ) c -= '0';

In this form, the for loop will create a reference for every character in the string, one at a time.

Now, what @JLBorges does, here, is a typical, old fashioned quick way of taking the character of a digit and turning into the number itself. It takes the character code '0' in the character set, and subtracts that from the value from the string. Now, '0' in the character set looks like a integer of 48, so this "c -= '0'" clause reduces 48 to zero, converting the ascii code for the digit into a number.

@JLBorges now treats the string as if it were an array of numbers. They're char's, so they're limited to a range of -127 to 127, but that's enough for your requirement (since you really only need 0 to 9).

This is now fed into a stl algorithm, which you might find foreign.

It does what you'd do more manually with:

1
2
3
4
5
6
7
8
9
10
11
int source_array[ NDIGITS ];
int multipliers[ NDIGITS ];

// whatever gets the user's entry into source_array, and declares the multipliers goes here

int csum = 0;

for( int i = 0; i < NDIGITS; ++i )
  {
   csum += ( source_array[ i ] * multipliers[ i ] );
  }


Now, this does, basically and simplistically, what "std::inner_product" is doing.

It is the heart of what you're asking. All those "+" in your example formula is handled by csum += ....

Each "i" in the array is the "ABCD...." in your list.

In @JLBorges version, those "ABCD..." entries from the user are in the string "str", converted into a kind of really short integer array.

Now, we look back at "is_valid".

@JLBorges uses two tests. First, if the size of the string is not NDIGITS, the return will be false and no need to look further. There must be NDIGITS in the user's input.

Second, @JLBorges uses the algorithm "all_of" to loop through each digit int the string that came from the user to be sure they are all digits.

You might do this in a much simpler way like this:

1
2
3
4
5
6
bool is_good = true;

for( int i = 0; i < str.length(); ++i )
{
 if ( str[ i ]  < '0' || str[ i ] > '9' ) is_good = false;
}


This is an old style, almost C approach that uses the kind of things new student programmers may recognize. This loops through each character in the string. The start of the code assumes this will all be good (set to true)...if any one of the characters is not a digit, "is_good" is made false, and the whole thing fails validation.

This is beginner level form. Each character must be between 0-9 inclusive...or, put the way it reads, it fails if the tested character is less than '0' or greater than '9'.

This could be done with std::isdigit( str[ i ] ) in that loop.

@JLBorges uses a modern C++ approach. First, the "all_of" algorithm will "process" all characters in the string string "str"....that's what this part does....

std::all_of( str.begin(), str.end().....

This sets up "all_of" to run from the beginning to the end of the string. The "std::all_of" function requires a function object of some kind. This is what "all_of" will do with each characters. It's as if you are required to give "all_of" a function it should call for every character, and the return value checked such that every function call returns true (or the whole thing returns false).

That's like the test I illustrate above....assume true, but any false makes the whole thing false.

...but what is the function to call?

In this case, @JLBorges uses a lambda expression - it's an "unnamed function written inline"....

It starts with "[]" - think of it as "lambda starts here"...

Next, is the same thing you see in a function declaration - it's the parameters the function takes. In this case, it's a character from the string.

Then, the function itself, which is just a call to "isdigit"....

Now, if you think about this walk through, you might take the more primitive versions I've put in this post, along with the modern C++ design @JLBorges posted, and probably come up with what you asked for....that "simpler" solution, that, really, isn't simpler...just more familiar to what you've covered in class.









> And shouldn't the pattern be 3, 7, 3 (not 3, 3, 7)?

Yes. I've corrected it now. Than you!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <valarray>
#include <string>
using namespace std;

int main()
{
   const int SIZE = 15;
   valarray<int> V(SIZE), M(SIZE);
   M = 3;   M[slice(1,SIZE/3,3)] = 7;

   string input;
   while ( input.size() != SIZE || input.find_first_not_of( "0123456789" ) != string::npos )
   {
      cout << "Enter a " << SIZE << "-digit code: ";
      getline( cin, input );
   }
   for ( int i = 0; i < SIZE; i++ ) V[i] = input[i] - '0';
   cout << "Output: " << ( V * M ).sum() << '\n';
}


Enter a 15-digit code: 123456789111213
Output: 230




It's more direct in fortran:
1
2
3
4
5
6
7
8
program main
   implicit none
   integer M(15), V(15)
   M = 3;   M(2::3) = 7
   print *, "Input a 15-digit code:"
   read( *, "( 15i1 )" ) V
   print *, "Output: ", sum( V * M )
end program main

or, if you want some (minimum) length and digit checking:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
program main
   implicit none
   integer, parameter :: SIZE = 15
   integer, dimension(SIZE) :: M, V
   character (len=SIZE) input
   character (len=20) fmt

   M = 3;   M(2::3) = 7
   write( fmt, "( '(',i0,'i1)' )" ) SIZE

   do while ( len_trim(input) /= SIZE .or. verify( input, "0123456789" ) > 0 )
      write( *, "( 'Input a ', i0, '-digit code: ' )", advance="no" ) SIZE
      read *, input
   end do

   read( input, fmt ) V
   write( *, "( 'Output: ', i0 )" ) sum( V * M )
end program main

Last edited on
And shouldn't the pattern be 3, 7, 3 (not 3, 3, 7)? It also might be reasonable to continue even if there are less than 3 digits. Missing digits would be 0's.

they both work. I found 3,3,7 easier to think through it. its just where you choose to start the sequence.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

int main()
{
  string input = "123456789012345"; //would be cin input
  char tbl[] = {3,3,7};
  int result = 0;
  for(int i = 1; i <= input.length(); i++) 
  {
	  result+= tbl[i%3]*(input[i]-'0');
      //cout << tbl[i%3] << endl; //validation 	  
  }
  cout << result << endl;
}
Last edited on
they both work. I found 3,3,7 easier to think through

I think you might have misunderstood what I was saying. I didn't say it very well, but JLBorges picked up my meaning anyway. What I meant was that this piece of his code that used to be like this:

 
return r1*3 + r2*3 + r3*7 +           // "pattern" is 3, 3, 7 

should in fact be like this:

 
return r1*3 + r2*7 + r3*3 +           // "pattern" is 3, 7, 3 

This is correct for his code which applies the factors from right-to-left through the digits. Nice piece of recursion based on the fact that the pattern repeats in 3's from the right.
Topic archived. No new replies allowed.