Author Topic: "Find 'nth' prime program" - Also, is this a good way to detect bad input? [C++]  (Read 3796 times)

0 Members and 1 Guest are viewing this topic.

Offline NoviParagon

  • /dev/null
  • *
  • Posts: 8
  • Cookies: 0
  • sal.paragon@gmail.com
    • View Profile
What is the best way of checking input from cin>> to see if it matches the target type & won't overflow? In the programs I've done so far with C++ I haven't really been checking this, and so if I enter a character like '+' or 'p' when I'm asking for a number, the program flips and usually gets stuck in a loop. I've been using cppreference.com to see what kind of functions I can use, I tried using cin.fail() (alongside clear and ignore) to check for mismatch types and it works mostly, but if a user enters more than one character like 'ep+rg'  then the loop runs for each invalid character which I rather dislike. If a mix of valid and invalid characters are entered such as "95ne" it can mess up the program since it'll run with the numbers and then with the text characters.

Also, I'm sure my method for finding primes is far from optimal, I tried to avoid just googling 'how to find primes quickly'. So I'm all for criticism/advice there.

Code: (cpp) [Select]
#include <iostream>
#include <ctime>

using std::cout; using std::cin; using std::endl;

bool isPrime(unsigned int givenNumber){        //actually determines if something is prime or not
    unsigned int divisor = 2;
    bool qPrime = true;
    while ((divisor < givenNumber) && (qPrime == true)){
        if (givenNumber % divisor == 0)
            qPrime = false;
        else
            ++divisor;
    }

    return qPrime;
}

int fnPrimes(int nthPrimeLim){    //interface for isPrime(), tests up to the nth prime set by the user in main --- see 'unsigned int recallNth'
   
    unsigned int nthPrime = nthPrimeLim;
    unsigned int testForPrime = 1;

    while (nthPrime > 1) {
        testForPrime += 2;
        if (isPrime(testForPrime) == true){
            --nthPrime;
            cout<< testForPrime << " is prime!\n";
        }
    }
    return testForPrime;
}

int main(){

    int recallNth = 0;
   
    char keepGoing = 'y';

    do {
        cout << "Enter a number 'n' to find the 'nth' prime: ";
        cin >> recallNth;        //tests up to this nth prime
        if (cin.fail()) {
            cin.clear();    //clear failure state
            cin.ignore();    //ignore and discard failed input
            cout << "\nInput failed. Try again" << endl;
            continue;        //bring us back to the top of do/while loop to ask again
        }
        cout << endl;
        cout << "We already know 2 is a prime...\n";

        std::time_t passedTime = std::time(NULL);    //set time for performance review
       
        cout << fnPrimes(recallNth) << " is the no. " << recallNth << " prime in the series of prime numbers.\n";    //notice fnPrimes called here, calls isPrime in itself
        cout << std::time(NULL) - passedTime << " seconds to solution.";

        cout << "\nTest again? ('y' for yes, anything else to quit): ";
        cin >> keepGoing;
    } while (keepGoing == 'y');

cin.ignore();
return 0;
}
« Last Edit: October 19, 2014, 06:50:05 pm by NoviParagon »

Offline HTH

  • Official EZ Slut
  • Administrator
  • Knight
  • *
  • Posts: 395
  • Cookies: 158
  • EZ Titan
    • View Profile
As I'm pressed for time right now I will only say this: for prime number testing look into Sieve's Algorithm.


Me I quite often use a statement of the structure


Code: [Select]
   
 bool inputGood = false;
 while (inputGood == false)
{
       cout << "Enter a number: " << flush;
       int n;
       if (cin >> n)
       {
                inputGood = true;
                // do something with n
        }
        else
       {
                cout << "Error! Ignoring..." << endl;
                cin.clear();
                cin.ignore(numeric_limits<streamsize>::max(), '\n');
        }
 }


However Ill be the first to admit that unless my code will come in contact with other people... i rarely validate anything Beyond the bare essentials
« Last Edit: October 19, 2014, 02:05:02 am by HTH »
<ande> HTH is love, HTH is life
<TurboBorland> hth is the only person on this server I can say would successfully spitefuck peoples women

Offline NoviParagon

  • /dev/null
  • *
  • Posts: 8
  • Cookies: 0
  • sal.paragon@gmail.com
    • View Profile
Ah, I didn't look into the arguments I could provide to cin.ignore(), that helped. And testing if(cin>>something) instead of cin.fail() is insightful, so thanks. As for the Sieve of Eratosthenes (?) , that looks pretty interesting and I'll try to implement it later, I really would like to see the compute time go down when trying to find n over 10k or so (it takes about 15 seconds for me on my computer to find n = 10001 using my own method).

I guess normally I wouldn't care about bad input for things like this but I really want to see if I can catch/correct them.

Offline HTH

  • Official EZ Slut
  • Administrator
  • Knight
  • *
  • Posts: 395
  • Cookies: 158
  • EZ Titan
    • View Profile
Yeah it was the one you found. I learned about it in a class it decreases the time to find a prime number by like 700%, but it doesn't scale well due to memory constraints. So if you had a reasonable upper limit on this program you could use it but not so much if the number is like 47352635947363643
<ande> HTH is love, HTH is life
<TurboBorland> hth is the only person on this server I can say would successfully spitefuck peoples women

Offline ArkPhaze

  • Peasant
  • *
  • Posts: 136
  • Cookies: 20
  • null terminated
    • View Profile
Even though you can use a sieve like atkins for example, you can still improve your prime checking function dramatically...

1. If you know the number is not 2, but divisible by 2, then it's not prime, why continue to check for divisibility by MULTIPLES of 2 redundantly?
2. You only need to check for divisors up to the sqare root of the number itself.
3. This is a function, why do you have a bool to set and return when you can just return true or false directly within the code?

Sidenote:
Code: [Select]
(qPrime == true)
Evaluating with the == operator to true is completely unnecessary for booleans.
Code: [Select]
(qPrime)
« Last Edit: November 03, 2014, 07:45:43 am by ArkPhaze »
sig=: ArkPhaze

[ J/ASM/.NET/C/C++ - Software Engineer ]

Offline Xires

  • Noob Eater
  • Administrator
  • Knight
  • *
  • Posts: 379
  • Cookies: 149
    • View Profile
    • Feed The Trolls - Xires
A quick note: if you're using VisualStudio(or possibly some other shoddy C++ implementations/compiler), be aware that the use of 'max()' may conflict with a macro.  You need to explicitly define that you're not using such built-in macros or compilation will fail.
-Xires

Offline ArkPhaze

  • Peasant
  • *
  • Posts: 136
  • Cookies: 20
  • null terminated
    • View Profile
A quick note: if you're using VisualStudio(or possibly some other shoddy C++ implementations/compiler), be aware that the use of 'max()' may conflict with a macro.  You need to explicitly define that you're not using such built-in macros or compilation will fail.

He didn't use 'max' anywhere..  ???
sig=: ArkPhaze

[ J/ASM/.NET/C/C++ - Software Engineer ]

Offline Xires

  • Noob Eater
  • Administrator
  • Knight
  • *
  • Posts: 379
  • Cookies: 149
    • View Profile
    • Feed The Trolls - Xires
numeric_limits<stream_size>::max() is a use of 'max()'.  Because the preprocessor runs through the source first, a macro would directly replace the call.

Code: (c++) [Select]
#define NAME DEFINITION

std::cout << FUNCTION_NAME() << std::endl;

The preprocessor will essentially turn that into FUNCTION_DEFINITION().
-Xires

Offline ArkPhaze

  • Peasant
  • *
  • Posts: 136
  • Cookies: 20
  • null terminated
    • View Profile
numeric_limits<stream_size>::max() is a use of 'max()'.  Because the preprocessor runs through the source first, a macro would directly replace the call.

Code: (c++) [Select]
#define NAME DEFINITION

std::cout << FUNCTION_NAME() << std::endl;

The preprocessor will essentially turn that into FUNCTION_DEFINITION().

Ah, I didn't look closely at the code, but I thought you were talking about std::min/std::max from <algorithm>, which would cause issues too.

Code: (c++) [Select]
#define NOMINMAX
#include <Windows.h>

He's not including that header though so he still should be fine..
« Last Edit: November 10, 2014, 04:33:19 am by ArkPhaze »
sig=: ArkPhaze

[ J/ASM/.NET/C/C++ - Software Engineer ]

Offline Xires

  • Noob Eater
  • Administrator
  • Knight
  • *
  • Posts: 379
  • Cookies: 149
    • View Profile
    • Feed The Trolls - Xires
Still, I figured it'd be best to know about it ahead of time so that if the issue came up, it'd be known.
-Xires

Offline NoviParagon

  • /dev/null
  • *
  • Posts: 8
  • Cookies: 0
  • sal.paragon@gmail.com
    • View Profile
Eh, sorry I didn't reply to this or update the post in a while, I've been busy. But I did implement a different method for finding primes using Eratosthenes' Sieve (the interface is also different), and the result is that it's WAYYYYYY faster, I can find primes up to the millions no problem, it's near instant.

This is the function I wrote for anyone interested, first you have to create a vector of ints from 2 to some n and pass by reference in main or some other function beforehand.

Code: (cpp) [Select]
/* "void primelist (vector<int>&)"
This function sieves through a vector of numbers 2-n and marks all non-primes by changing them to 0.
The zeroes can then be ignored/removed elsewhere in the program.
*/

void primeList(std::vector<int> &rvPrimes){
    int loc = 0;    //current place in vector
    int pVal;
    while (loc < rvPrimes.size()){
        if (rvPrimes[loc] == 0)
            loc++;
        else {
            pVal = rvPrimes[loc];
            for (int i = 1; (loc + (i * pVal)) < rvPrimes.size(); ++i){
                rvPrimes[loc + (i * pVal)] = 0;
            }
            loc++;
           
        }
    }
    std::cout << "done!\n";
}
« Last Edit: November 11, 2014, 07:22:46 am by NoviParagon »

Offline Xires

  • Noob Eater
  • Administrator
  • Knight
  • *
  • Posts: 379
  • Cookies: 149
    • View Profile
    • Feed The Trolls - Xires
Some constructive criticism & a few pointers to help improve the code:

First, try not to use std::cout from a function outside of main() unless it is specifically intended for I/O.  I know this doesn't seem to matter much to people these days but it's a design concept that is much preferred, especially for professional-level code.  Another note, for the same design reasoning, is that you should always try to 'return' from your code, even if it's void.  A simple 'return;' statement is sufficient and lets various code-scanning tools(and documentation tools) better handle the function.

Second, your flow logic could be improved.  Since both the true and false forks of your conditional check result in a 'loc' increment, you should keep it outside the condition.  Even better, since the true fork does nothing but the increment, you can reverse the logic and eliminate it altogether.

Code: (c++) [Select]
while (loc < rvPrimes.size()) {
  if (rvPrimes[loc] != 0) {  // OR if (rvPrimes[loc]) since it's a boolean check against value
    pVal = rvPrimes[loc];

    for (int i = 1; (loc + (i * pVal)) < rvPrimes.size(); ++i)
      rvPrimes[loc + (i * pVal)] = 0;
  }

  ++loc;
}

Third, since 'loc' and 'i' are both used for indexes, their value should never be negative.  Thus, you would be best to use 'unsigned int' for these variables.  Further, note that std::vector<T>.size() returns an unsigned value(size_type is unsigned) and thus (int + (int * int)) should return an int and int < unsigned int should technically throw a compiler warning.  The reason for this is that it is possible to have a vector that has more than 2,147,483,647 members(though not necessarily wise).  If this is the case, then (loc + (i * pVal)) could potentially ALWAYS be < rvPrimes.size() and you're stuck with an infinite loop.

Fourth, your inner iterator could potentially be implemented as a register allowing for a tighter loop.  The compiler may do this automatically but not always.  Don't abuse the register keyword, as there are only a few registers available at one time, but rather use it sparingly where you can potentially reap a large benefit.  A single inner loop that runs many times over each element of a list that can measure a million or more in size is a very good reason to use a register.

Finally, rather than leaving values in the vector as 0, it might be best to remove the entry completely to keep a cleaner and smaller vector for speed and memory efficiency.  This may not be important to this function specifically, or even this test-case, but if you're planning to use this function in other code where you want a list of primes to pass around, it might be best to use as little memory as possible.  Further, if you're appending to the vector in a separate thread(using intelligent queuing with proper lock handling; vectors by themselves are not necessarily thread-safe!!) then its size can obviously grow quite large rather quickly.

An example including some of the aforementioned suggestions:
Code: (c++) [Select]
#include <iostream>
#include <vector>

// shortcut macro for uncluttered calculation
#define C(l, i, p) (l + (i * p))

void primeListing(std::vector<int> &rvPrimes) {
  unsigned int loc = 0, sz = rvPrimes.size();
  int pVal;  // could pVal legitimately be negative???

// shortcut macro using local variables for uncluttered calculation
#define D(i) C(loc, i, pVal)

  while (loc < sz) {
    if (rvPrimes[loc]) {  // true if rvPrimes[loc] has non-zero value
      pVal = rvPrimes[loc];

      for (register unsigned int i = 1; D(i) < sz; ++i)
        rvPrimes[D(i)] = 0;
    }

    ++loc;
  }

  // optional; eliminates 0 values, leaving a clean list of only primes
  for (
    std::vector<int>::iterator it = rvPrimes.begin();
    it != rvPrimes.end();
    ++it
  )  // I know this looks weird but some still use 80-column terminals
    if (!*it) rvPrimes.erase(it--);  // post-decrement

  return;
}

int main(void) {
  std::vector<int> primes;

  for (unsigned int i = 2; i < 1000; ++i) primes.push_back(i);

  primeList(primes);

  for (
    std::vector<int>::iterator it = primes.begin();
    it != primes.end();
    ++it
  )  // again, looks weird but it's easy to read and understand in 80 cols
    std::cout << *it << std::endl;

  return 0;
}

Overall, this is a good exercise and a worth-while pursuit.  I would recommend continuing to work on improving this code to see how efficient & elegant you can make it.  If you have questions, just voice them and I'm sure someone will assist.
-Xires

Offline NoviParagon

  • /dev/null
  • *
  • Posts: 8
  • Cookies: 0
  • sal.paragon@gmail.com
    • View Profile
Thanks for the tips Xires, this improved function is gonna be very useful since a bunch of Euler problems deal with primes. I took your advice and modded it a bit. I have the primelist function contained in its own hpp/cpp file for other programs. Here is how the program for this specific problem looks now.

Euler7.cpp
(contains main)
Code: (cpp) [Select]
#include <iostream>
#include <vector>
using std::cout; using std::cin; using std::endl;

#include "primelister.hpp"

int main(){

    unsigned int recallNth = 0;
    std::vector<int> vPrimes;
    char keepGoing = 'y';

    cout << "Setting aside memory...";
    vPrimes.reserve(9999999);           //it's cool to use reserve() with push_back() for vecs
    for (int i = 2; i < 10000001; i++)
        vPrimes.push_back(i);
    cout << "\nCreating list of primes up to 10 million...";
    primeList(vPrimes);
    cout << "Done! size of list is " << vPrimes.size() << ".";

    do {
        cout << "\nEnter a number 'n' to find the 'nth' prime: ";

        cin >> recallNth;        //goes to this nth position in the vector, so long as it is a valid number
        if (cin.fail() || recallNth > vPrimes.size() || recallNth < 0) {
            cin.clear();    //clear failure state
            cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');    //ignore and discard failed input
            //limits of           maximum input stream size, and newline

            cout << "\nInput failed. Trying again..." << endl;
            continue;        //bring us back to the top of do/while loop to ask again
        }
       
        cout << endl;
        switch (recallNth) {
        case 0:
            cout << "Can't test for 0th prime!";
            break;
        default:
            cout << vPrimes[recallNth - 1] << " is the no. " << recallNth << " prime in the series of prime numbers.\n";
            break;
        }

        cout << "\nTest again? ('y' for yes, anything else to quit): ";
        cin >> keepGoing;
    } while (keepGoing == 'y');

    cin.ignore();
    return 0;
}

And then the code for primeList. I didn't use your method for removing the marked 0s, Xires (although I like it), but instead took advantage of the algorithm library and the erase-remove idiom. I also decided not to use the register keyword, because from what I researched online, the compiler will use the cpu registers automatically, and usually with better selection than a human. (I am using the most recent VC++ compiler in VS2013)
primelist.cpp
Code: (cpp) [Select]
#include <algorithm>
#include <vector>
#include "primelister.hpp"

/* "void primelist (vector<int>&)"
This function sieves through a vector of numbers 2-n and marks all non-primes by changing them to 0.
The zeroes can then be removed or ignored. In this implementation, they will be removed before the function exits.
*/

#define C(l, i, p) (l + (i * p))    //N.B.: refers to (loc + (i * pVal))

void primeList(std::vector<int> &rvPrimes){
    unsigned int loc = 0, sz = rvPrimes.size();    // 'loc' is current place in vector
    int pVal;

    #define D(i) C(loc, i, pVal)

    while (loc < sz){
        if (rvPrimes[loc]){
            pVal = rvPrimes[loc];
            for (unsigned int i = 1; D(i) < sz; ++i)
                rvPrimes[D(i)] = 0;
        }
        loc++;
    }
    rvPrimes.erase(std::remove(std::begin(rvPrimes), std::end(rvPrimes), 0), std::end(rvPrimes));    //erase-remove idiom; removes all 0s
    rvPrimes.shrink_to_fit();    //free up a little memory by reducing capacity() down to actual size()

    return;
}

and, if anyone is interested, the header file for primelist:
primelist.hpp
Code: (cpp) [Select]
#ifndef PRIMELISTER_HPP
#define PRIMELISTER_HPP

void primeList(std::vector<int>&);

#endif
« Last Edit: November 12, 2014, 02:02:48 am by NoviParagon »

Offline Xires

  • Noob Eater
  • Administrator
  • Knight
  • *
  • Posts: 379
  • Cookies: 149
    • View Profile
    • Feed The Trolls - Xires
Excellent.  Very well done.

I figured I'd introduced enough new stuff that I didn't want to bother using anything from <algorithm> and certainly not boost but I am glad that you had the insight to look into it and implement a fairly decent method on your own.  Regarding the register usage; I often forget people even bother using VisualStudio for C/C++ as it is a completely illogical choice in my mind.  However, I have seen several instances where specifying a variable as register-qualified has a poor impact(like crashing, system freezes, etc.) on MSVC code so it's likely a good decision to let the compiler decide in this case.

Something else to consider; would this ever be intended to function using negative integers?  If not, std::vector<unsigned int> might be preferred.  If you are using or permitting negative integers then how would that effect 'i' as both an iterator and part of the sieve calculation?
« Last Edit: November 12, 2014, 04:52:02 am by Xires »
-Xires

Offline NoviParagon

  • /dev/null
  • *
  • Posts: 8
  • Cookies: 0
  • sal.paragon@gmail.com
    • View Profile
You're right about std::vector<unsigned int>; I have no intention of using this to generate negative primes, and if I did for some reason want negative primes I would probably just create a new vector where these already-generated primes are altered to be negative - and then if I wanted I could merge the two together into a single vector of primes (-n to n). 

As for using MSVS, well I got the professional version for free (insert joke about free food tasting better), and it's required by Unreal Engine's editor, something else I'm learning to use, which I also got for free...I use it because I really like the IDE, and support for C#/VC# but because I'm fresh I realise there may be much better options out there, maybe you can point me in the right direction?

Anyways, I'm pleased with the useful discussion generated on this thread, I think in the future I'll post more Euler problem solutions and other coding problems, as I don't make the most efficient or neat code the first time through, I'm happy to see the green 'check'! But I always like to look back on my solutions afterwards to try and make them better.
« Last Edit: November 13, 2014, 02:28:02 am by NoviParagon »