tags:

views:

1298

answers:

17

Assume n is odd and is taken as user input.

Can you write C++ code that takes the number n (being odd)

and creates a star hill. For instance, assume n is 5 the output would be:

*
***
*****
***
*

Could this entire hill be generated using 2 for loops?

I havent done C++ in over 12 years and was playing with it... I was able to do:

*
***
*****

Using a couple of for loops but was not able to figure out how to complete this simple challenge using only 2 for loops...I thought i could control the rows (n) and j could control the columns (*) but Im having an issue going backwards....

+7  A: 

Perhaps you could do a loop from -h to h and use abs.


In the 5 case, mentally convert:

1 3 5 3 1

To:

5-4 5-2 5-0 5-2 5-4

And then you have a loop:

int h = n - 1;
for (int i = -h; i <= h; i += 2)
    ;//draw n - abs(i) stars
Chris
(oops, someone wrote that already!)
Chris
A: 

Actually, you can do it completely without for loops if you define the problem recursively.

Anyways... If you go for the looping version, let i loop through 0..N-1, and then have an inner loop printing N-2*abs(i-N/2) stars.

norheim.se
+1  A: 

easy:

int i;
int n;

for(i = 0; i < 1; i++){
    for(n = 0; n < 1; n++){
        std::cout << "*\n***\n*****\n***\n*"<< std::endl;
    }
}

Okay, so that example was a little extreme in that it doesn't work. Here's another extreme, using one loop with no actual statements in its body:

#include<iostream>

void makeHill(int);

int main(){

  std::cout<< "Making hill of size 6: "<< std::endl;
  makeHill(6);
  std::cout<< "\nMaking hill of size 3: "<< std::endl;
  makeHill(3);

  return 0;

}

void makeHill(int n){

  int reachedMax = 0;
  int numberOfStars = 0;
  int printingStars = 0;
  int starsLeft = 0;
  int i = 0;

  while( ( ! printingStars) ?
     ( ( ! reachedMax) ?
       ( ( ++numberOfStars == n - 1) && (reachedMax = 1) )
       : --numberOfStars ), printingStars = 1, starsLeft = numberOfStars
     : ( ( ! starsLeft ) ?
         printingStars = 0, (std::cout<< std::endl), 1
         : --starsLeft, (std::cout<< "*"), 1 ) ); //I love C(++)

  std::cout<< std::endl;

}

It works except that it skips the second row of stars for some reason.
It's different from the main example in that it doesn't only work for odd numbers (doesn't go 1-3-5-3-1, but 1-2-3-4-5-4-3-2-1).

And I know I could probably make the loop better and smaller, but I just hacked it together until it worked.

~/c/t $ ./hill
Making hill of size 6:
*
***
****
*****
******
*****
****
***
**
*

Making hill of size 3:
*
***
**
*

I somehow get the feeling that I'm no longer welcome in the programming community...

Carson Myers
funny but unrealistic!!!
JonH
although, I guess mine doesn't take a number. Missed that part.
Carson Myers
also your program fails when n is 7 :)...erm doesn't fail but the output isn't right :)
JonH
it will work now :)
Carson Myers
I think my soul vomited while I read your statement-less while loop version.
Ben S
I had to wear earmuffs while I wrote it because my brain kept trying to escape through my ears to escape the torture.
Carson Myers
+1  A: 
for (int i = 1; i <= n; ++i)
  {
  for (int j = 1; j <= min(i, (n-i)+1); ++j)
    {
    std::cout << "*";
    }
  std::cout << std::endl;
  }
mLewisLogic
The indexing could probably be optimized, but I feel it's easier to read for this particular problem.
mLewisLogic
I havent done C++ in over a decade what exactly does min() do ?
JonH
@JonH http://www.cplusplus.com/reference/algorithm/min/ "Return the lesser of two arguments"
JeffH
+15  A: 

I use the absolute function to get the symmetry. This essentially prints out a variable number of * depending on what line we're on relative to the longest line. The further you are, the fewer * you print.

void printHill(int height) {
    for (int i = 1; i < height * 2; i++) {
        for (int j = 0; j < height - abs(height - i); j++) {
            std::cout << "*";
        }
        std::cout << std::endl;
    }
}
Ben S
yep this would do it!man i miss C++ :)
JonH
ChrisW
@ChrisW: Yeah, that's how I coded originally, but the question asked a way to do this using two loops.
Ben S
Close, but you have to adjust your increment. Each line should only have an odd number of *
csj
A: 

This is my solution using a single loop:

int n = 5;
string s = "";
for (int i = 0; i < n*2; i++) {
    if (i<n) {
        s += "*";
    } else {
        s = s.substr(1,s.length());
    }
    if (i % 2) continue;
    std::cout << s.c_str() << endl;
}

cin >> n;  //Just to hold the console open to view my results.
DMKing
+4  A: 

One (visable) loop:

void hill(int h)
{
    for(int loop = -h +1;loop < h;loop += 2)
    {   std::cout << std::string( h - (loop<0?-loop:loop) ,'*') << "\n";
    }
}

Or for even more fun a version with no loops.

std::string hs  = "*\n***\n*****\n*******\n*********\n***********\n*************\n";
void hill2(int h)
{
    int len  = (h*h+2*h+1)/4;
    int e    = (h+1)/2;
    std::cout << std::string(hs.begin(),hs.begin()+ (len + e))
              << std::string(hs.rend() - (len - h - 1 + e - 1) ,hs.rend())
              << "\n";
}
Martin York
I ended up writing nearly the same thing - yours seems obfuscated to me, and I didn't recognize it.
Mark Ransom
A: 

How about using ZERO loops?

Sorry I had to write this in C# to test it (no gcc or VC++ on this machine), but the algorithm still holds up for C++

class Program
{
    static void Main(string[] args)
    {
        drawHill(5, 1, false);
        Console.ReadLine();
    }

    void drawHill(int maxHeight, int start, bool peakReached)
    {
        if (start > 0)
        {
            drawRow(start);
            Console.WriteLine();

            if (start == maxHeight)
                peakReached = true;

            if (peakReached)
            {
                drawHill(maxHeight, --start, peakReached);
            }
            else
            {
                drawHill(maxHeight, ++start, peakReached);
            }
        }
    }

    void drawRow(int num) 
    {
        if (num > 0) 
        {
            Console.Write("*");
            drawRow(--num);
        }
    }
}
free-dom
A: 
#define HEIGHT 10
int main()
{
  char buffer[HEIGHT+1] = {0};
  memset(buffer, '*', HEIGHT);   
  for(int i=HEIGHT; abs(i)<=HEIGHT; --i)
  {
    printf("%s\n", buffer + abs(i));
  }
  std::cin.get();
  return 0;
}
Krypes
`#define` to define a literal constant in C++? `printf` and `std::cin` in the same function? Ow...
Pavel Minaev
Wish you could downvote comments.
Blindy
+1  A: 

I mean, 2 loops is nice and all, and the recursion was sort of inventive, but how about doing it with 1 loop using nothing but math and some pointer arithmetic.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

void hill(int max)
{
   char *buff, *p;
   int i, n;

   p = buff = (char *)malloc(max * (max + 1) * sizeof(char));
   memset(buff, '*', max * (max + 1));

   for(i = 0; i < max; ++i)
   {
      n = max - (2 * abs(i - (max - 1) / 2));
      p[n] = '\0';
      printf("%s\n", p);
      p += max + 1;
   }

   free(buff);
}

int main()
{
   hill(5);
   return 0;
}
ReaperUnreal
FYI: `sizeof(char)` always equals 1. The standard defines it that way. Also, why use `malloc()` in C++?
Whisty
I'm working in a dev compiler on an old mainframe, sizeof(char) isn't always 1. Also I'm in C not C++, you might notice that I'm also using printf, stdio.h, etc.
ReaperUnreal
+27  A: 

Because it's C++, the real way to solve this kind of problem is at compile-time with templates:

#include <iostream>
using namespace std;

template <int NStars> struct DrawLine {
    static void Go() {
     cout << "*";
     DrawLine<NStars - 1>::Go();
    }
};

template <> struct DrawLine<0> {
    static void Go() {
     cout << endl;
    }
};

template <int NRows, int IRow> struct DrawHill {
    static void Go() {
     DrawLine<(IRow < (NRows / 2) ? IRow + 1 : NRows - IRow)*2 - 1>::Go();
     DrawHill<NRows, IRow+1>::Go();
    }
};

template <int NRows> struct DrawHill<NRows, NRows> { static void Go(){} };

int main(int argc, char* argv[]){
    DrawHill<5, 0>::Go();
    return 0;
}

Which nicely compiles down to code with absolutely no run-time conditional branches.

Of course if you only know the number at runtime, you'll have to do some more work:

#define DRAWHILL(n) case n: DrawHill<n,0>::Go();break
void DrawTheHill(int nRows)
{
    switch(nRows)
    {
     DRAWHILL(1);
     DRAWHILL(3);
     DRAWHILL(5);
     DRAWHILL(7);
     DRAWHILL(9);
     DRAWHILL(11);
     DRAWHILL(13);
     DRAWHILL(15);
    default:
     throw runtime_error("Fail");
    }
}

int main(int argc, char* argv[]){
    DrawTheHill(15);
    return 0;
}

Disclaimer: Don't do this in any real code. This is a terrible way to do it.

Eclipse
I was hoping someone would do this!
mtnygard
+1 for templates, but what if the number is only known at run-time?
Bill
If it's only known at run-time, then you'll just have to pre-compile every possible input number and choose at run-time which one to run.
Eclipse
+1 for the effort put into it, and for the fact its always nice to see clever use of templates
Viktor Sehr
Where's to button to award the poster 1 internets?
Palad1
Funny. But also sometimes a good solution to a problem... or not. I recall a programming competition once, where some students precomputed several thousand ground answers to a recurive computation. Great run-time performance... but it took half an hour to compile (this was in 1998). Next year, there was a rule that all code had to compile in less than a minute.
jakobengblom2
A: 
void print_stars ( int n )
{
    if ( ( n < 1 ) || ( ( n & 1 ) == 0 ) ) return;

    std::string buf( ( n * ( 3 * n + 6 ) + 2 ) / 6, '*' );

    for ( int i = 1, j = 2; j <= n; j += 2, i += j ) 
        buf [ i ] = buf[ buf.size() - 1 - i ] = '\n';

    std::cout << buf << std::endl;
}

You could get it down to four lines if you removed the check whether n is positive and odd, but then it might break.

Pete Kirkham
+3  A: 

Why use a loop... when you can use recursion?!*

#include <iostream>

void step(int x)
{
    if (x > 0)
    {
        std::cout << "*";
        step(x-1);
    }
    else
    {
        std::cout << std::endl;
    }
}   

void hill_(int cur, int lim, bool going_up)
{
    if (cur > 0)
    {
        step(cur);
        cur += going_up ? 2 : -2;
        hill_(cur, lim, cur == lim ? !going_up : going_up);
    }   
}

void hill(int lim)
{
    hill_(1, lim, true);
}

int main()
{
    hill(5);
    std::cout << "--------" << std::endl;
    hill(9);
}

Output:

*
***
*****
***
*
--------
*
***
*****
*******
*********
*******
*****
***
*

*If you make this stack overflow, it's not like the output would have fit on your terminal anyway.

Mark Rushakoff
Cool I was wanting to see a recursive solution.
DMKing
+1  A: 

This version is incredibly simple, only one loop. I suppose there's a hidden loop in the string constructor, but that doesn't count.

void Hill(int n)
{
    for (int i = -n+1;  i < n;  i += 2)
    {
        std::string stars(n - abs(i), '*');
        std::cout << stars << std::endl;
    }
}
Mark Ransom
+2  A: 

Here ya go, I was bored. I used recursion.

void drawHill(int hillSize, int currentRow) {
    cout << string(currentRow, '*') << endl;
    if(currentRow < hillSize) drawHill(hillSize, currentRow+2);
    if(currentRow != hillSize) { cout << string(currentRow, '*') << endl; }
}

EDIT: Got a stack overflow at hillSize = 6893

Graphics Noob
+13  A: 

Some fun with GCC's error messages...

// going up to the tip...
#line 1
template<typename T, int N, int M = N, bool up = true>
struct hill {
  typedef typename hill<T**, N, M - 2>::next next;
};

#line 1
//  ... on the tip! down now again ...
template<typename T, int N>
struct hill<T**, N, 1, true> {
  typedef typename hill<T, N, 3, false>::next next;
};

#line 1
// going down...
template<typename T, int N, int M>
struct hill<T**, N, M, false> {
  typedef typename hill<T, N, M + 2, false>::next next;
};

// preparing for the finish...
#line 1
template<typename T>
struct ThatWasTheHill { };

template<typename T>
struct TheFollowingIsTheHill 
{ typedef typename ThatWasTheHill<T>::next next; };

#line 1
template<typename T, int N>
struct hill<T*, N, N, false> 
{ typedef typename TheFollowingIsTheHill<T>::next next; };


// start off!
hill<int*, 7> h;

Works with 3 upwards...

main.cpp: In instantiation of 'TheFollowingIsTheHill<int>':
main.cpp:3:   instantiated from 'hill<int*, 7, 7, false>'
main.cpp:4:   instantiated from 'hill<int***, 7, 5, false>'
main.cpp:4:   instantiated from 'hill<int*****, 7, 3, false>'
main.cpp:4:   instantiated from 'hill<int*******, 7, 1, true>'
main.cpp:3:   instantiated from 'hill<int*****, 7, 3, true>'
main.cpp:3:   instantiated from 'hill<int***, 7, 5, true>'
main.cpp:3:   instantiated from 'hill<int*, 7, 7, true>'
main.cpp:7:   instantiated from here
main.cpp:6: error: no type named 'next' in 'struct ThatWasTheHill<int>'

This is the result with the brand new bleeding edge clang compiler, using -fno-caret-diagnostics to prevent it mixing source code in between :)

main.cpp:6:44: error: no type named 'next' in 'ThatWasTheHill<int>'
main.cpp:3:51: note: in instantiation of template class 'struct TheFollowingIsTheHill<int>' requested here
main.cpp:4:51: note: in instantiation of template class 'struct hill<int *, 7, 7, 0>' requested here
main.cpp:4:51: note: in instantiation of template class 'struct hill<int ***, 7, 5, 0>' requested here
main.cpp:4:47: note: in instantiation of template class 'struct hill<int *****, 7, 3, 0>' requested here
main.cpp:3:46: note: in instantiation of template class 'struct hill<int *******, 7, 1, 1>' requested here
main.cpp:3:46: note: in instantiation of template class 'struct hill<int *****, 7, 3, 1>' requested here
main.cpp:3:46: note: in instantiation of template class 'struct hill<int ***, 7, 5, 1>' requested here
main.cpp:7:15: note: in instantiation of template class 'struct hill<int *, 7, 7, 1>' requested here

Maybe you are lucky and it works with your compiler too :)

Johannes Schaub - litb
Very nice! +1 Best abuse of the compiler
Eclipse
+1 only because I can't upvote more than once.
Eduardo León
thanks folks :)
Johannes Schaub - litb
+1 for uniqueness!
LiraNuna
A: 

Here's a solution that genuinely uses one loop (unless you decide to count addition and multiplication as having loops) :

#include <iostream>
using namespace std;
int main(void) {
    int H, i, r, d=3, dd=1;
    cin >> H;
    r = 1;
    for (i=0; i < H*H+2*H-1; i++) {
     if (i<r)
      cout << '*';
     else {
      cout << '\n';
      r += d;
      if (d > H)
       dd = -1;
      d += dd;
     }
    }
    return 0;
}
Joey Adams