No Fun with Roman Numerals

I was going over the ICT Proficiency sample exam earlier because apparently I’ve got to take it pretty soon — nevermind that I’m a design guy, and not, you know. A geek. Yeah, go figure. But wish me luck, too, because this is what I’ll have to deal with:

Hobson's Choice

Hobson's Choice

Needless to say, this had me silently cursing: PHP I can do just fine; and I’m still in awe of the possibilities with Python (I always end up thinking: whoa… you can do that in Python?! Is this a noob thing? Because so many things are just darn easy in Python!). But I haven’t even looked sideways at any C code since 1997, never got over my fear of Java, and the last version of Visual Basic I used was VB4. Oh, and I don’t even know what COBOL code looks like. So yeah, SOL. I’ve decided to go the C/C++ route because it’s the most familiar, and for the life of me, I cannot do the simplest things in Java without needing to look something up. Same goes, strangely enough, for VB.

So anyway, here’s the sample problem from NCC:

Write a program that will convert a Roman number into its equivalent Hindu Arabic number, and vice versa. The program should ask the user what type of number to convert (Roman or Hindu Arabic).

Hindu to Roman is pretty simple, and I started visualizing a switch block in my head when it hit me: Dictionaries. Does C++ have those? I’m still not sure, but I’m pretty sure C doesn’t, so I went with two arrays for the conversion table instead. I was eager to get my feet wet, and here’s where Ubuntu really pisses me off: doesn’t a Linux distro, y’know, have to have a C/C++ compiler pre-installed? I wouldn’t know, Ubuntu and Mint are the only ones I’ve tried, but you’d think, right? So, a sudo aptitude install build-essential later and I felt good to go. And a funny thing happened.

See, I wrote a short pseudocode on some paper scraps, and I noticed: damn, that almost looks like Python code. And what’d it hurt, right? I’d try to get it running on Python to see if it works first, then translate it to C/C++. That way, I’d only be worrying about one thing at a time. Python it is for the moment, then.

So Hindu to Roman was relatively straightforward, though in retrospect I’ve thought of a better implementation, but I digress. I had some real trouble with Roman to Hindu, though. My problem was with those pesky IV’s, IX’s, XL’s and such, because I’d originally planned to read the characters one by one and just add them all up. But that won’t work with those special cases, so (here it comes) I thought: regex.

Right. Several minutes later I was kicking myself, too. There’s got to be another way!

And it’s really obvious, but it wasn’t to me until I started looking for patterns, so in a way, trying to use regular expressions helped. In a really roundabout way. See, it’s only in those special instances (XC, XL, IV, IX, etc) that a lower value comes before a higher one (reading left to right). Everything else, it’s VIII, XI, LX and such. And in those instances, it basically means that the left value is subtracted from the right value, so in IV, 1 is subtracted from 5 to produce 4, and so on. I took a moment to congratulate myself until I realized that that’s really how Roman Numerals are translated in the first place. So yeah, fail.

So tomorrow I’m gonna take this code and try to translate it to C/C++. Gah, I feel like gouging my eyes out already.

def hindu_to_roman(num):
	if not 0 < num < 4000:
		return "Invalid input."

	temp = num
	roman = ""
	i = 0
	romans = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
	numbers = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
	while temp > 0:
		if temp < numbers[i]:
			i += 1
		elif temp >= numbers[i]:
			roman += romans[i]
			temp -= numbers[i]

	return roman

def roman_to_hindu(num):
	romans = ["M", "D", "C", "L", "X", "V", "I"]
	numbers = [1000, 500, 100, 50, 10, 5, 1]
	total = 0
	temp = num.upper()

	for i in range(len(temp)):
		try:
			currval = numbers[romans.index(temp[i])]
			if i < len(temp) - 1:
				nextval = numbers[romans.index(temp[i+1])]
				if currval < nextval:
					currval *= -1

			total += currval
		except:
			total = "Invalid input."
	return total

def main():
	choice = input("[1] Roman to Hindu\n[2] Hindu to Roman\n[3] Quit\nChoose: ")
	while choice != 3:
		if choice == 1:
			num = raw_input("Enter a valid Roman numeral. Please? : ")
			res = roman_to_hindu(num)
			print res
		elif choice == 2:
			num = input("Enter a positive integer less than 4000: ")
			res = hindu_to_roman(num)
			print res
		elif choice == 3:
			pass
		else:
			print "\nInvalid choice.\n"

		choice = input("\n[1] Roman to Hindu\n[2] Hindu to Roman\n[3] Quit\nChoose: ")

main()

Updated: Now with C++ procedural code

#include <iostream>
#include <cctype>
#include <string>

using namespace std;

int roman_to_hindu(string);
string hindu_to_roman(int);

int roman_to_hindu(string toConvert){
	const int ARRSIZE = 7;

	int intlist[ARRSIZE] = {1000, 500, 100, 50, 10, 5, 1};
	char charlist[ARRSIZE] = {'M', 'D', 'C', 'L', 'X', 'V', 'I'};
	int total = 0;
	string upper = toConvert;

	// convert to uppercase
	for(int i = 0; i < toConvert.length(); i++){
		upper[i] = toupper(toConvert[i]);
	}

	// create temporary storage
	int* temp = NULL;
	temp = new int[upper.length()];

	// cycle every item in upper
	for( int i = 0; i < upper.length(); i++ ){
		// for every item in upper, cycle charlist
		for( int j = 0; j < ARRSIZE; j++ ){

			if( upper[i] == charlist[j] ){
				temp[i] = intlist[j];
			}
		}

	}

	// cycle temp
	for( int i = 0; i < upper.length(); i++ ){
		int currval = temp[i], nextval = temp[i+1];
		// make sure the test doesn't exceed the array size
		if( currval < nextval && i < upper.length() - 1){
			temp[i] *= -1;
		}
		total += temp[i];
	}

	return total;
}

string hindu_to_roman(int iConvert){
	const int ARRSIZE = 13;
	int temp = iConvert;
	string total;

	int intlist[ARRSIZE] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
	string charlist[ARRSIZE] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

	int i = 0;
	while( temp > 0 ){
		if( temp < intlist[i] ){
			i++;
		}
		else{
			total += charlist[i];
			temp -= intlist[i];
		}

	}

	return total;

}

int main(void){

	int iChoice;

	do{
		cout << "[1] Roman to Hindu\n[2] Hindu to Roman\n[3]Quit\n\nChoose: ";
		cin >> iChoice;

		iChoice = (iChoice > 3 || iChoice < 1) ? 3 : iChoice;

		switch(iChoice){
			case	1:
				{
					string toConvert;

					cout << "Enter a valid Roman numeral: ";
					cin >> toConvert;

					cout << "\n" << toConvert << " converted to Hindu numerals is: " << roman_to_hindu(toConvert) << "\n" << endl;
					break;
				}
			case	2:
				{
					int toConvert;

					cout << "Enter a positive integer: ";
					cin >> toConvert;

					cout << "\n" << toConvert << " converted to Roman numerals is: " << hindu_to_roman(toConvert) << "\n" << endl;
					break;
				}
		}

	}while(iChoice != 3);
	cout << "\nBye!" << endl;
	return 0;
}

Related posts:

  1. More practice: sorting
  2. Storing a CSV text file into a C Struct
  3. Quick and Dirty Test for Primes
  4. Technology and Me
  5. Annuntio vobis gaudium magnum. Habemus Papam!

7 Comments

10:15 am Sunday, 15th 2009f February, 2009
Blind

limitation ba ng requirement na less than 4000?

kasi sabi sa tanong e
“Write a program that will convert a Roman number into its equivalent Hindu Arabic number, and vice versa. The program should ask the user what type of number to convert (Roman or Hindu Arabic).”

wala namang sinabi na less than 4000 lang ang input…

baka madale ka sa numbers greater than 4000 (yung mga may bar sa taas nung roman numerals)

10:23 am Sunday, 15th 2009f February, 2009

kasi wala namang roman numeral above 4000 e :p haka haka lang ang mga un (at sige nga, pakita mo saken pano lagyan ng bar ang M sa text na terminal ;p)

10:26 am Sunday, 15th 2009f February, 2009

btw blind..na flag ka as spam.. ^^

10:29 am Sunday, 15th 2009f February, 2009
Blind

di naman kelangan ilagay yung bar

gagawin ko nang condition yung input sa Roman to Hindu:

Is your input greater than 4000? (Y/N)

tapos
if y then input roman numeral
else
input roman numeral

gawa ka ng dalawang procedure, yung sa Y e times 1000 sa input

yung N yung original procedure mo.

11:05 am Sunday, 15th 2009f February, 2009

bat pa dalawang procedure? tanggalin ko lang ang if not 0 < num < 4000 automatic nang magdidisplay ng maraming M :p

ang point don kasi hanggang 3999 lang ang classic roman number system kaya linagyan ko ng limit

8:01 am Tuesday, 19th 2009f May, 2009

Low Sir Jeorge. Great programming skills talaga. :)

See you tomorrow on the testing center. I am very afraid to take the examination. But I’ll be somehow read and follow your programming logic on the patterns. Thanks for the code.

5:06 pm Saturday, 5th 2009f September, 2009
Jonathan Thompson

Wish I’d seen this code before my job interview :P

There is one definite bug in it, though: you need to delete[] temp because it leaks!

Post a Comment

*
*

By submitting a comment here you grant this site a perpetual license to reproduce your words and name/web site in attribution.