Tell me more ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

An undulant number is a number where its digits go up and down like the following number: 461902 or 708143, or even 1010101, but not 123, because 2 < 3. write function which returns true if a number is undulant, false otherwise. The shortest function wins.

NOTE: For completeness, single digit numbers are accepted by the function and the function would return false (I imagine this case as non-undulant number), thus isUndulant returns false for n < 10.

share|improve this question
Number input as string, integer, float...? – leftaroundabout Jul 11 '11 at 19:07
1  
What's the objective here? Code-golf (shortest function)? – Alexandru Jul 11 '11 at 20:22
1  
@JBernardo: I would think True or undefined behavior, as it would be a better base case for recursion. – Joey Adams Jul 11 '11 at 21:26
3  
Your definition of undulant number is not in agreement with the standard definition: mathworld.wolfram.com/UndulatingNumber.html. Is this intentional? – mellamokb Jul 14 '11 at 12:03
3  
My solution could be 16% smaller if the base case were true (as would make sense IMHO). – eternalmatt Jul 15 '11 at 23:22
show 6 more comments

35 Answers

1 2

J, 45

*./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=.

Sample use:

   *./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 461902
1
   *./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 708143
1
   *./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 1010101
1
   *./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 123
0
   *./(n>9),(}:(=-)}.)(}:*@-}.)n#:~10$~>.10^.n=. 5
0

I'm pretty sure there's a finer way of twisting Insert / to do more of the work in a go, but I've been J-less for months, I need to get back to it.

share|improve this answer
It will be hard to beat J in this problem. nice solution! – leonardo Jul 13 '11 at 15:21
@leonardo thanks! – J B Jul 13 '11 at 16:15

Ruby, 72 70 characters

Q=10;k=->n,v{(n%Q-n/Q%Q)*v<0?k[n/Q,-v]:n<Q};u=->n{n>9&&k[n,-1]|k[n,1]}

Usage and testcases:

p u[10101]   # <= true
p u[708143]  # <= true
p u[2421]    # <= false
p u[1231]    # <= false
p u[873]     # <= false

Single digits yield false:

p u[5]       # <= false

Consecutive identical digits also return false:

p u[66]      # <= false
p u[1221]    # <= false
share|improve this answer

Haskell, 88 characters

q[]=9<3
q[e]=e/=EQ
q(a:b:s)=a/=EQ&&a/=b&&q(b:s)
w s=zipWith compare s$tail s
u=q.w.show
share|improve this answer
I am so furious that you beat my 104 char solution. u n=length s>1&&(a[GT,LT]||a[LT,GT])where s=show n;z=q compare s$tail s;w=q(==)z;q=zipWith;a=and.w.cycle It's kinda elegant. zipWith once with compare like you did, then zipWith again with (==) and cycle[GT,LT] or cycle[LT,GT] as second arg. – eternalmatt Jul 15 '11 at 23:07

Python: 101 100 characters

Before minification:

undulate = (lambda n: n > 9
            and all(cmp(*digits) == (i % 2) * 2 - 1
                    for i, digits
                    in enumerate(zip(min(`n`,`n`[1:]), 
                                     max(`n`,`n`[1:])))))

After minification:

a=lambda b:b>9and all(cmp(*c)==d%2*2-1 for d,c in enumerate(zip(min(`b`,`b`[1:]),max(`b`,`b`[1:]))))
share|improve this answer

Sage, 76 83char

f=lambda x:uniq(cmp(*`x`[i-2:i][::(-1)^i])for i in[2..len(`x`)])in[[1],[-1]]

Got the idea to use cmp(*[..]) from JBernardo. In Sage, uniq(...) is an alias for list(set(...)).

Edit: just noticed that for x < 10, uniq(cmp(...)) == [], which isn't on [[1],[-1]]. If x were input as a string, instead of an integer, I could get another 4 characters out!

share|improve this answer
I had an idea to use sum(uniq(...))^2, since sum([1,-1]) = 0, and the sums of the singletons [1] and [-1] squares to 1. Unfortunately, it fails on a thrice-repeated digit; 1011101. – boothby Jul 12 '11 at 16:58
Nice. I should learn sage. BTW, I just realized that backticks will append an L if the number is bigger than 2**32 in Python and affects the result. Does that happens on Sage? – JBernardo Jul 12 '11 at 20:02
Yeah, Sage makes a few things nice for golfing... for instance, its ridiculous startup time is spent importing a huge tree of modules. The Sage Integer class doesn't bother with the L because Sage is preparsed python; 1234 -> Integer('1234'). You can jump right into using Sage here: sagenb.org – boothby Jul 12 '11 at 20:29

J, 30

*/0<(#,]*{.*1 _1$~#)2-/\a.i.":

A different approach than the other J answers.

   */0<(#,]*{.*1 _1$~#)2-/\a.i.":461902
1
   */0<(#,]*{.*1 _1$~#)2-/\a.i.":708143
1
   */0<(#,]*{.*1 _1$~#)2-/\a.i.":1010101
1
   */0<(#,]*{.*1 _1$~#)2-/\a.i.":123
0
   */0<(#,]*{.*1 _1$~#)(}.-}:)a.i.":5
0

Would be 3 characters shorter if 5 were considered undulant.

share|improve this answer
Well, at least I can console myself with the thought that I had the lead for an hour. :-) – Gareth Mar 24 '12 at 20:58

(pdf)eTeX, 129 chars

\def\a#1#2{\if#2?\ifx\r\s\def\s{1}\else
True\end\fi\fi\edef\t{\pdfstrcmp{#2}{#1}}\ifx\s\t
False\end\fi\let\s\t\a#2}\expandafter\a

Compiling with pdfetex filename.tex 1324? gives a pdf output. TeX is primarily a typesetting language, and outputting instead to stdout would take around 20 more chars. Also the strange requirement for one-digit numbers (false rather than true) takes me 26 chars.

share|improve this answer

Python, 134 129 chars

def f(x):d=[cmp(*i)for i in zip(`x`,`x`[1:])]if x>9 else[0];n=d[0]>0;return all(i<0 for i in d[n::2])&all(i>0 for i in d[n<1::2])

Ungolfed:

def f(x):
    if x>9:
        d = [cmp(*i)for i in zip(`x`,`x`[1:])] #difference of x[i] and x[i+1]
    else:
        d = [0]       #trick to return False if x<10 using less chars
    n = d[0]>0        #First digit is -1 or 1?
    neg = d[n::2]     #negative numbers if x is Undulant
    pos = d[not n::2] #positive numbers if x is Undulant

    #check if all negs are -1 and all pos are 1 and return value
    return all(i<0 for i in neg) and all(i>0 for i in pos)
share|improve this answer

JavaScript, 88 chars

function _(i){i+='';c=i[0];f=i[a=x=1];for(g=f<c;d=i[x++];c=d)g^=a&=g?d<c:d>c;return!f^a}

In essence, turn the number into a string and compare adjacent characters, flipping the expectation for each.

share|improve this answer
2  
In JavaScript, a function doesn't need a name and the question asks explicitly for a function, so you can save two characters. – rynah Jul 13 '11 at 14:34

Python, 119 108

def u(x):l=[cmp(i,j)for i,j in zip(`x`,`x`[1:])];print x>9and all([i*j<0 for i,j in zip(l,l[1:])])and l!=[0]
share|improve this answer
2  
Nice use of xor. You can cut quite a few characters out with ... for a,b in zip(t,t[1:]) rather than using ranges. Also, you don't need the brackets in all([...]) -- Python makes a generator when it finds (... for ...), even if the parentheses are for a function call. – boothby Jul 14 '11 at 7:50
Thank you very much for your advice! They have been very valuable! -20 chars – 0xKirill Jul 15 '11 at 8:12
Very nice solution. Few more characters x>9 and all(i^j for i,j in zip(l,l[1:])) and remove if l else False. – Ante Jul 16 '11 at 17:09
1  
It is not working in all cases. Two cases are problematic: only 2 digits (e.g. 11), and last 2 digits are same and larger than one before (e.g. 12155). First problem is since there is no testing if x<100. Second is because 'one way comparison'. It can be fix with cmp(i,j) and instead i^j set i*j<0, and testing and l[0]!=0. Few more characters :-/ – Ante Jul 16 '11 at 17:24
1  
Hmmm... print saves one character over return, but is it legitimate? The spec does ask for a function that "returns". – Gareth Rees Jul 17 '11 at 17:03
show 1 more comment

Python 105 101 100 chars

c=lambda r,t:len(r)<2 or(cmp(*r[:2])==t and c(r[1:],-t))
u=lambda x:x>9and c(`x`,cmp(*`x`[:2])or 1)

Recursive solution. c(r,t) checks if first char of r is less (t==-1) or greater (t==1) of second char, and call opposite check on shortened string.

share|improve this answer
Nice. You can save a character in the first line by removing 0, and you can save three characters on the second line by writing u=lambda x:x>9 and c(`x`,cmp(*`x`[:2])or 1) – Gareth Rees Jul 17 '11 at 17:14
Tnx. I didn't like any() from the beginning :-) – Ante Jul 17 '11 at 20:31
You can save one more by writing x>9and. – Gareth Rees Jul 17 '11 at 21:35

J, 44 39 36 31 characters

*/2(0<#@],0>*/\)*2-/\".;' ',.":

Usage as before.

I hadn't noticed that my last edit made the inequality with 0 check completely unnecessary. :-)

Previous answer (+ explanation):

(0=+/2=/\u)*(1<#u)**/2~:/\2<:/\u=.".;' ',.":

Usage:

    (0=+/2=/\u)*(1<#u)**/2~:/\2<:/\u=.".;' ',.":461902
1

The answer has four parts:

  1. u=.".;' ',.": This reads in the number as a string ":, splits it into a list of characters preceded by spaces ' ',., stitches it back together ;, converts it back to numbers ". and then stores the result u=. This basically turns 461902 into 4 6 1 9 0 2 which I find easier to process in J.

  2. */2~:/\2<:/\ This operates on the value stored in u. It takes each pair of characters and checks if the left one is less than or equal to the right one 2<:/\ so 4 6 1 9 0 2 becomes 1 0 1 0 1. It then takes the result of this and checks each pair of numbers for inequality 2~:/\ so 1 0 1 0 1 becomes 1 1 1 1. Finally it multiplies them all together to get either a 0 or a 1 */ At this point we could return the answer if it weren't for 2 things: a single digit returns 1 when the question requires a 0; and equal numbers are treated the same as 'less than' so 461900 returns 1 instead of 0. Bummer. On we go...

  3. (1<#u) This checks if the number of items stored in u #u is greater than 1 and returns false if it's just a single digit number.

  4. (0=+/2=/\u) This takes each pair of numbers stored in u and checks for equality 2=/\u. It then sums the answers and checks if it has 0.

The results of parts 2, 3 and 4 are then multiplied together to (hopefully) produce a 1 when the number meets the requirements specified in the question.

share|improve this answer
Nice job re-taking the lead, but I just borrowed a trick from yours! – ephemient Mar 25 '12 at 17:56
(That being said, I think you could take my a.i.": to shave a few more characters off.) – ephemient Mar 25 '12 at 18:03
Unfortunately, I'm probably going to have to put that inequality check back in - my answer fails now for 11, 22, 33, 44 etc. – Gareth Mar 25 '12 at 19:57

CoffeeScript, 98 67 53

(n)->0!in((n[i]>=c^(n[0]<n[1])+i)%2for c,i in n[1..])

tests:

[
    '01010101' # true
    '12345'    # false
    '1010101'  # true
    '887685'   # false
    '9120734'  # true
    '090909'   # true
]

uncompressed:

undulant = (n) ->
    direction = n[0] < n[1]
    return n.split('').every (cur, i) ->
        prev = arr[i-1] or 10 * direction
        +(prev >= cur) is (direction+i)%2
share|improve this answer

k (41 chars)

{(x>9)&~max(=). 1_'-':'1_'(<':;>':)@\:$x}

eg.

{(x>9)&~max(=). 1_'-':'1_'(<':;>':)@\:$x}1212130659
1b
share|improve this answer

Python, 155 chars

g=lambda a,b:all(x>y for x,y in zip(a,b))
u=lambda D:g(D[::2],D[1::2])&g(D[2::2],D[1::2])
def U(n):D=map(int,str(n));return(n>9)&(u(D)|u([-d for d in D]))
share|improve this answer

C++, 94 chars

bool u(int N){int K,P,Q,U=1,D=1;while(N>9)P=N%10,Q=(N/=10)%10,K=D,D=U&Q<P,U=K&Q>P;return U^D;}

same method as my Erlang awnser with a for loop rather than recursion.

share|improve this answer

Haskell, 82 characters

c=cycle[(<),(>)]
l!n=n>9&&and(zipWith3($)l(show n)$tail$show n)
u n=c!n||((>):c)!n
share|improve this answer
I count only 83 characters in this solution. (Are you on Windows, perhaps? Write the file with unix line endings, which is legal Haskell.) – MtnViewMark Jul 16 '11 at 16:00
Thanks, I was using 'wc' to count my characters on Cygwin. I count 82 characters. I used the following code, as wc seems to be outputting an extra character. (Vim doesn't show a trailing newline, but notepad does...) readFile "Undulant.hs" >>= print . length . dropWhile (== '\n') . reverse . filter (/= '\r') – Thomas Eding Jul 18 '11 at 23:11

Golfscript, 48

Hoping to beat J, my first time using Golfscript. Didn't quite succeed.

[`..,(<\1>]zip{..$=\-1%.$=-}%(\{.@*0<*}/abs
share|improve this answer

Perl/re, 139

Doing everything in regex is kind of a bad idea.

/^(?:(.)(?{local$a=$1}))?(?:(?>((.)(?(?{$a lt$3})(?{local$a=$3})|(?!)))((.)(?(?{$a gt$5})(?{local$a=$5})|(?!))))*(?2)?)(?(?{pos>1})|(?!))$/

I'm using Perl 5.12 but I think this will work on Perl 5.10. Pretty sure 5.8 is out though.

for (qw(461902 708143 1010101 123 5)) {
    print "$_ is " . (/crazy regex goes here/ ? '' : 'not ') . "undulant\n";
}

461902 is undulant
708143 is undulant
1010101 is undulant
123 is not undulant
5 is not undulant
share|improve this answer

PowerShell, 88

Naïve and trivial. I will golf later.

filter u{-join([char[]]"$_"|%{if($n){[Math]::Sign($n-$_)+1}$n=$_})-notmatch'1|22|00|^$'}

My test cases.

share|improve this answer

JavaScript, 112

function(n,d,l,c,f){while(l=n%10,n=n/10|0)d=n%10,c?c>0?d>=l?(f=0):(c=-c):d<=l?(f=0):(c=-c):(c=d-l,f=1);return f}

You only need to pass it one argument. I could probably golf this further with a for loop.

share|improve this answer
(d>=l -> d>0) and (d<=l -> d<2) perhaps? I'm not looking closely, as perhaps d might contain fractional parts that might skew it. – Thomas Eding Jul 18 '11 at 23:16
@trinithis: That's a lowercase L, not a 1. Thanks though! – rynah Jul 18 '11 at 23:54
Where's DejaVu Sans Mono or Bitstream Vera Sans Mono when you need it? Perhaps I need to customize stackoverflow with some custom css or a user script... – Thomas Eding Jul 19 '11 at 0:46
@trinithis: I agree, the font choice isn't that great. Bolding doesn't stand out enough... – rynah Jul 19 '11 at 4:07

Erlang, 137 123 118 chars

u(N)->Q=N div 10,u(Q,N rem 10,Q>0,Q>0). u(0,_,D,U)->D or U;u(N,P,D,U)->Q=N rem 10,u(N div 10,Q,U and(Q<P),D and(Q>P)).
share|improve this answer
Won't this return True so long as there has been at least one up and one down transition anywhere? Won't it return True for, say 1234321? – MtnViewMark Jul 12 '11 at 2:36
@ MtnViewMark, yeah it did thanks, I misunderstood the question fixed now hopefully. – Bunnit Jul 12 '11 at 3:33

Q, 71

{$[x>9;any all a=#[;(1 -1;-1 1)](#)a:1_signum(-':){"I"$x}each -3!x;0b]}

sample usage

q){$[x>9;any all a=#[;(1 -1;-1 1)](#)a:1_signum(-':){"I"$x}each -3!x;0b]} 5
0b
q){$[x>9;any all a=#[;(1 -1;-1 1)](#)a:1_signum(-':){"I"$x}each -3!x;0b]} 10101
1b
q){$[x>9;any all a=#[;(1 -1;-1 1)](#)a:1_signum(-':){"I"$x}each -3!x;0b]} 01010
1b
q){$[x>9;any all a=#[;(1 -1;-1 1)](#)a:1_signum(-':){"I"$x}each -3!x;0b]} 134679
0b
q){$[x>9;any all a=#[;(1 -1;-1 1)](#)a:1_signum(-':){"I"$x}each -3!x;0b]} 123456
0b
q){$[x>9;any all a=#[;(1 -1;-1 1)](#)a:1_signum(-':){"I"$x}each -3!x;0b]} 132436
1b
share|improve this answer
You can logic away the if {(x>9)&any all a=#[;(1 -1;-1 1)](#)a:1_signum(-':)("I"$')($)x} gives 62 – slackwear Mar 23 '12 at 19:07
Never seen ($) syntax for string before and the logic is a nice touch. – tmartin Mar 26 '12 at 8:41

perl, 78 chars

sub u{@_=split//,$_=shift;s/.(?=.)/($&cmp$_[$+[0]])+1/ge;chop;$#_&&!/00|1|22/}
share|improve this answer

Scala 141, 133, 129 97:

def u(n:Int):Boolean=n>9&&{
val a=n%10
val b=(n/10)%10
a!=b&&n<99||(a-b*b-(n/100)%10)<0&&u(n/10)}

With a = n % 10, b = (n/10) % 10, c = (n/100) % 10

if a > b and b < c or 
   a < b and b > c

Then a-b * b-c is either x*-y or -x*y with x and y as positive numbers, and the product is in both cases negative, but for -x*-y or x*y (a < b < c or a > b > c) the product is always positive.

The rest of the code is handling special cases: one digit, two digits, two identical digits.

share|improve this answer
def is_undulant(n):
   digits = [int(i) for i in list(str(n))]
   diffs = []
   for i in range(len(digits) - 1):
        diffs.append(cmp(digits[i], digits[i+1]))
   if len(diffs) == 1:
       if diffs[0] == 0:
           return False
   else:
       for i in range(len(diffs) - 1):
           if diffs[i] * diffs[i+1] != -1:
               return False
   return True
share|improve this answer
if you're writing Python2, you can use cmp instead of your diff function – JBernardo Jul 11 '11 at 20:55
Thanks, I had forgotten there is 'cmp'. – Tigrux Jul 12 '11 at 17:01
Aghh... I had not realized that the goal is to write in as few characters as possible. :-/ – Tigrux Jul 12 '11 at 17:05
digits = map(int,str(n)) – st0le Jul 15 '11 at 12:46

PHP, 151 chars

function i($n){$n=str_split($n);@$p=$n[1]==null?:($m=($n[0]>=$n[1])?-1:1)*10;foreach($n as$k=>$v)if(($k%2*2-1)*($v-$p)*@$m<=0&$p=$v)return!1;return!0;}

ungolfed(-ish):

function is_undulant($n){

      $n=str_split($n);
      @$p=$n[1]==null?:($m = ($n[0] >= $n[1])? -1 : 1)*10;
      foreach ($n as$k=>$v)
        if ( ($k%2 * 2 - 1) * ($v - $p) * @$m <= 0 & $p=$v ) return !1;

      return !0;
}
share|improve this answer

Different approach I think. 124 chars

def undulant(x):
    x = `x`
    return any((int(x[1])-int(x[0]))*-1**i*(int(x[i])-int(x[i-1]))>0 for i in range(2,len(x)))
share|improve this answer
You should tell us what language that is. – user unknown May 4 '12 at 13:01

Scala, 194 characters

def u(i:Int):Boolean={val x=i.toString;if(!x.matches("(\\d)\\1")){"(?<=.*)(\\d)(?=(\\d).*)".r.replaceAllIn(x,m=>if(m.group(1)<m.group(2))"<"else">").matches("((<>)+<?)|((><)+>?)\\d")}else false}

Hmm...regex seemed like a good idea at the time...

share|improve this answer
Doing everything in regex is kind of a bad idea. (Your comment inspired me!) – ephemient Mar 25 '12 at 5:40

C# 147 142

bool U(int i){if(i<10)return 0>1;var s=i+"";var b=s[0]>s[1];for(int j=2;j<s.Length;j++){var x=s[j-1]>s[j];if(x==b)return 0>1;b=x;}return 1>0;}

Working sample: http://ideone.com/b8ubr

share|improve this answer
1 2

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.