Today's puzzler should be a rather simple one, but I'm curious to see how folks solve it. Don't forget - these Friday Puzzler's are meant to be quick tests of your ColdFusion coding skills. If you can't solve it in five minutes, than take a break. (If I get someone fired I'll feel so guilty. At least for 30 seconds.) So what's the challenge?
Given two numbers, M and N, return the fraction created by dividing M into N, but only if a 'whole' fraction is created. Otherwise return an empty string.
Examples:
(2,4) == 1/2
(3,9) == 1/3
(5,25) == 1/5
Assume, or throw an error, if the first number is larger than the second. Support from 1/1 to 1/9 for full "bonus" points. (Sorry, bonus points are not redeemable on planets with oxygen.)
Ok, get to it!
Archived Comments
Does orbiting a planet count as being on that planet? ;-)
function reduceFraction(a,b) {
if(a gt b) throw("omgwtf");
var c = b / a;
if(int(c) eq c) return (a/c) & "/" & (b/c);
return "";
}
Love the error message Rick. :)
Please see my solution. This may not be a good one.
<cfset a = 50>
<cfset b = 50>
<cfif a GT b>
<cfthrow message="a cannot be greater than b">
</cfif>
<cfif a EQ b>
<cfoutput>1</cfoutput>
<cfabort>
</cfif>
<cfif (b MOD a) EQ 0>
<cfoutput>1/#b/a#</cfoutput>
<cfelse>
<cfoutput>Not a perfect fraction</cfoutput>
</cfif>
Oh blarg. Strike that entry. It doesn't do anything like what you asked.
<cffunction name="stupidFractionThing">
<cfargument name="m" type="numeric" required="true" />
<cfargument name="n" type="numeric" required="true" />
<cfset var ret = "" />
<cfset var check = 0 />
<cfif n EQ 0>
<cfthrow message="Silly developer, you can't divide by zero" />
</cfif>
<cfif m GT n>
<cfthrow message="You can't do that because Ray said not to allow it.">
</cfif>
<cfset check = 100/((m/n) * 100) />
<cfif check EQ int(check)>
<cfset ret = "1/"&check />
</cfif>
<cfreturn ret />
</cffunction>
Is that what you mean?
<cfscript>
arrVorlage=array(array("2","4"),array("3","9"),array("5","25"));
for(a=1; a lte arraylen(arrVorlage);a++){
writeoutput("(#arrVorlage[a][1]#,#arrVorlage[a][2]#)==#fractions(arrVorlage[a][1],arrVorlage[a][2])#<br>");
}
function fractions(m,n){
var f=0;
var returnvalue="0";
if(m gt n) return returnvalue;
if(n mod m neq 0) return returnvalue;
f=n/m;
return "1/#f#";
}
</cfscript>
@Peter: That array() function - isn't that Railo only?
function factors(n) {
// return a structure of the factors of the number
var i = 2;
var f = structNew();
while(i lte n) {
if((n mod i) eq 0) {
n = n / i;
if(not structKeyExists(f,i)) f[i] = 0;
f[i] = f[i] + 1;
i = 1;
}
i = i + 1;
}
return f;
}
function combineFactors(s) {
// recombine the factors into the reduced number
var r = 1;
var k = "";
for (k in s) {
r = r * (k ^ s[k]);
}
return r;
}
function reduceFraction(a, b) {
var f = factors(a);
var g = factors(b);
var k = "";
var c = 0;
for(k in f) {
if(structKeyExists(g,k)) {
// eliminate common factors
c = min(f[k], g[k]);
f[k] = f[k] - c;
g[k] = g[k] - c;
}
}
a = combineFactors(f);
b = combineFactors(g);
return a & "/" & b;
}
Ray, does the function need to deal with cases like "(4,10) == 2/5" or just cases where the numerator is 1?
Never mind... just saw Rick's solution. He was thing the same way I was...
It just needs to handle cases where the numerator is 1.
Probably not the easiest way, but...
<cfif IsDefined("FORM.submit")>
<cfif FORM.m GT FORM.n>
<p>Error!</p>
<cfabort>
</cfif>
<cfif SQR(FORM.n) EQ FORM.m>
<cfoutput><p>1/#FORM.m#</p></cfoutput>
<cfelse>
<p>Error!</p>
</cfif>
</cfif>
(Assuming there's a form there for numbers m and n.)
Yes array() is railo only; pherhaps openBD,too.
But the array() has no effect for the solution.
If you need a adobe-like solution. Let it know me ;-)
Thought I'd give it a try:
<cffunction name="reduceFraction" access="public" returntype="string">
<cfargument name="m" required="true" type="numeric" />
<cfargument name="n" required="true" type="numeric" />
<cfset var numerator = arguments.m />
<cfset var denominator = arguments.n />
<cfset var fraction = "#numerator#/#denominator#" />
<cfset var i = 0 />
<cfset var gcd = 1 />
<!--- Throw message if numerator is greater than denominator. CMS --->
<cfif m GT n>
<cfthrow message="(m) cannot be larger than (n)." />
<cfelseif n MOD m EQ 0>
<cfset denominator = n / m />
<cfset numerator = 1 />
<cfreturn "#numerator#/#denominator#" />
</cfif>
<!--- Find the greatest common denominator. CMS --->
<cfloop from="#numerator#" to="1" index="i" step="-1">
<cfif numerator MOD i EQ 0 AND denominator MOD i EQ 0>
<cfset gcd = i />
<cfbreak />
</cfif>
</cfloop>
<cfset numerator = numerator / gcd />
<cfset denominator = denominator / gcd />
<cfreturn "#numerator#/#denominator#" />
</cffunction>
function fractionalize(m,n)
{
var v = gcd(m,n);
if(m>n)
return "not!";
else
return "#m/v# / #n/v#";
}
function gcd(m,n)
{
if(m == 0)
return n;
else if(n < m)
return gcd(n,m);
else
return gcd(n mod m, m);
}
i'll admit i stole gcd from one of my own old computer science assignments
<cfscript>
function math(m,n) {
if(m gt n or m lt 1 or m gt 9 or n lt 0 or n gt 9)
r = 'bad input';
else if(n mod m eq 0)
r='1/#n/m#';
else
r='not whole fraction';
return r;
}
for(i = 1; i lte 9; i++)
for(j = i; j lte 9; j++)
writeoutput('m=#i#, n=#j#: #math(i,j)#<br>');
</cfscript>
<cfimport prefix="g" taglib="cfgroovy/tags" />
<g:script>
variables.reduce = { int n, int d ->
def factor = ((BigInteger) n).gcd(d);
return n / factor + "/" + d / factor;
}
</g:script>
#reduce.call(2, 4)#<br />
#reduce.call(3, 9)#<br />
#reduce.call(5, 25)#<br />
#reduce.call(49, 98)#<br />
@Barney:
Show off.
;)
Bah. Just because I'd rather use a method that Sun wrote and unit tests instead of my own (likely jacked) code, doesn't mean I'm a showoff. But if you prefer CFML you can do it like this:
<cfscript>
function reduce(n, d) {
var factor = createObject("java", "java.math.BigInteger").init(n).gcd(createObject("java", "java.math.BigInteger").init(d));
return n / factor & "/" & d / factor;
}
</cfscript>
Such a mess with all those createObject() calls though.
<cffunction name="fractionAsString">
<cfargument name="numerator"
type="numeric"
required="true">
<cfargument name="denominator"
type="numeric"
required="true">
<cfset var strFraction = "" />
<cfif numerator gt denominator>
<cfset strFraction = "Error: Numerator and Denominator do not create a whole fraction." />
<cfelse>
<cfif (denominator mod numerator) EQ 0>
<cfset strFraction = "1/#numerator mod denominator#" />
</cfif>
</cfif>
<cfreturn strFraction />
</cffunction>
<cfoutput>
#fractionAsString(4,16)#
</cfoutput>
10 minute solution:
<cffunction name="fractionAsString">
<cfargument name="numerator"
type="numeric"
required="true">
<cfargument name="denominator"
type="numeric"
required="true">
<cfset var strFraction = "" />
<cfset var isValid = false />
<cfif arguments.denominator eq 0>
<cfset strFraction = "Error: Divide by zero." />
<cfelseif abs(arguments.numerator) gt abs(arguments.denominator)>
<cfset strFraction = "Error: Numerator and Denominator do not create a whole fraction." />
<cfelse>
<cfif (abs(arguments.denominator) mod abs(arguments.numerator)) EQ 0>
<!--- If the fraction equals 1 --->
<cfif (abs(arguments.numerator) mod abs(arguments.denominator)) EQ 0>
<cfset strFraction = "1" />
<cfelseif abs(numerator) EQ 1>
<cfset strFraction = "1/#abs(arguments.denominator)#" />
<cfelse>
<cfset strFraction = "1/#abs(arguments.numerator) mod abs(arguments.denominator)#" />
</cfif>
<cfset isValid = true />
</cfif>
</cfif>
<!--- Add negative sign if either numerator or denominator are negative values --->
<cfif isValid AND (arguments.numerator LT 0 OR arguments.denominator LT 0)>
<cfset strFraction = "-" & strFraction />
</cfif>
<cfreturn strFraction />
</cffunction>
<cfoutput>
[#fractionAsString(6,5)#]<br />
[#fractionAsString(5,6)#]<br />
[#fractionAsString(1,5)#]<br />
[#fractionAsString(5,5)#]<br />
[#fractionAsString(-4,16)#]<br />
[#fractionAsString(0,0)#]<br />
</cfoutput>
I will stop here as I see one logic error in my code for handling negative numbers. -4/-16 = 1/4 not -1/4.
That is what I get for a 10 minute solution that needed further testing. ;)
Barney-
That's awesome. I bow before you.
Rick,
Too funny =)
I looked briefly at the logic error:
<cfif isValid AND (arguments.numerator LT 0 OR arguments.denominator LT 0)>
Needs to be:
<cfif isValid AND (arguments.numerator LT 0 XOR arguments.denominator LT 0)>
A legit usage of XOR. Now that is geeky. =)