-4

Need help creating a vertical histogram with "#" for the number of rolls I get. It will have a max of 80 characters. This is what I have so far. I can get everything to print the way I need it to except I'm having a lot of trouble with my histogram.

import random
from collections import defaultdict

def main():
    dice = int(input("Enter the number of dice: "))
    sides = int(input("Enter the number of sides: "))
    rolls = int(input("Enter the number of rolls to simulate: "))
    result = roll(dice, sides, rolls)
    for i in range(dice, dice * sides + 1):
        print('{:2d}{:10,d}{:10.4%}'.format(i, result[i], result[i] / rolls))
    print(histogram(result, dice, sides, rolls))

def roll(dice, sides, rolls):
    d = defaultdict(int)
    for i in range(rolls):
        d[sum(random.randint(1, sides) for i in range(dice))] += 1
    return d

def histogram(result, dice, sides, rolls):
    maxBar = str(80)
    for x in result:
        p = str(x)
        if p <= maxBar:
            p += '#'
    return p

main()

Output example::

   5          7  0.0070% 
   6         64  0.0640% 
   7        191  0.1910% #
   8        429  0.4290% ###
   9        942  0.9420% #######
  10      1,629  1.6290% ############
  11      2,701  2.7010% #####################
  12      3,911  3.9110% ###############################
  13      5,375  5.3750% ##########################################
  14      6,849  6.8490% ######################################################
  15      8,383  8.3830% ##################################################################
  16      9,371  9.3710% ##########################################################################
  17     10,051 10.0510% ################################################################################
  18      9,972  9.9720% ###############################################################################
  19      9,453  9.4530% ###########################################################################
  20      8,371  8.3710% ##################################################################
  21      7,022  7.0220% #######################################################
  22      5,517  5.5170% ###########################################
  23      3,824  3.8240% ##############################
  24      2,586  2.5860% ####################
  25      1,661  1.6610% #############
  26        936  0.9360% #######
  27        462  0.4620% ###
  28        195  0.1950% #
  29         78  0.0780% 
  30         20  0.0200% 
user3040301
  • 39
  • 2
  • 9

2 Answers2

1

If I were you, I'd use a dictionary to track results. For each result you have, cast it to a string and check to see if that string is in the dictionary (alternatively, use a try:except block and catch KeyError exceptions), if not then add it, if so then increment it. Here's some sample code:

num_dice = getNumDice() #however you're doing this
num_sides = getSides() #however you're doing this
num_rolls = getRolls() #however you're doing this

result_dict = {}

for _ in xrange(num_rolls):
  result = str(rollDice(num_dice,num_sides)) #you can build this yourself, I'm not doing everything for you! :)
  if result in result_dict:
    result_dict[result] +=1
  else:
    result_dict[result] = 1

#alternatively, using try/catch, see the commented code below
#in place of the if/else block:
#
#  try:
#    result_dict[result] +=1
#  except KeyError as e:
#    result_dict[result] = 1
#------------------------------------------------------------

This will populate your result_dict with something like {'2': 5, '3': 8, '4': 12, '5': 26, '6': 51, '7': 92, '8': 50, ... , '12': 9}. You should be able to poll that data to print your output. If you need more help than that, please comment with some trial code and I'll troubleshoot for you.

EDIT WITH YOUR REQUEST FOR A HISTOGRAM IS BELOW

For your histogram, you should return a function that maps the number of "#'s" you need to the number you need it on. Getting the correct ratio is pretty simple cross-multiplication, actually.

sum_of_result   num_of_hashes
------------- = -------------
 total_rolls     total_hashes

If you pass these variables to the histogram function, it should be able to return an array with how many #'s should be next to each result, that you can incorporate into your print function. Let's give it a whirl.

def histogram(roll_array,total_hashes=80): 
# that second argument lets us reuse this code for a
# differently scaled histogram without changing our
# code -- this is HUGE in coding.

  total_rolls = sum([roll_array[i] for i in range(len(roll_array))])
# sums up all the results across the array. We could also
# pass this directly to histogram, but (as before) this makes
# the code a little more reusable.

  output_array = defaultdict(int)
  for i in range(len(roll_array)):
    output_array[i] = roll_array[i]*total_hashes/total_rolls
#   this iterates through the output_array, setting each index equal
#   to the number of hashes we should use to accurately describe
#   each result, using the math I talked about earlier.

  return output_array

Now we've got a function, histogram (probably better described as make_histogram or etc) that returns an array (actually a dictionary, but one that functions like an array) of equal length to the array of dice rolls it was passed. Each index in that array has a value that correlates to how many hash marks it will take to accurately display that, given the quantity expressed in the roll_array (which is, in your main function, d)

Now for your print function. You know how multiplication works with strings, right? If not, go into IDLE and find out right now. Type "I Shall Always Experiment With Code\n" 50 times, then try multiplying it by 50.

Cool, right? Now instead of adding a second print statement at the end of main, try using the histogram's return array (again, it's a dictionary, but we don't need to care about that right now since all the keys are numbers just like indexes in an array) to display those #'s in-line with your current print function.

Also, that's kind of a discrete function. May be worth separating it out of your main function and make a def display_result(roll_array,hist_array): or something similar.

Again, if you need help, just ask.

Here is my complete code for this problem, for any future stackers :)

Adam Smith
  • 52,157
  • 12
  • 73
  • 112
  • Thank you so much. How would you print it the way the output is supposed to look though? – user3040301 Dec 04 '13 at 19:29
  • Just saw your update for the histogram. If I were you, I'd use String.format() like you're doing in your above example, however your final line `print(histogram(result, dice, sides, rolls))` won't work as intended, since it will create another set of lines underneath. I'll toss together something in an edit to my answer. Before I do, a little clarification. How does the histogram scale? Are there 80 "#'s"? Does the top answer have 80 "#'s?" It affects the histogram function (which is simple math, you should be able to figure that out on your own ;D) – Adam Smith Dec 04 '13 at 20:58
  • @user3040301: if you're completely stuck at any point, I've included my [complete code for this function](http://pastebin.com/K6Nkjwey) but please don't just copy this and go for it. It contains very useful lessons about string formatting, using basic math to solve complex problems, and etc. – Adam Smith Dec 04 '13 at 21:58
  • 1
    I have learned so much from you. Thank you so much. Most helpful response I have ever gotten! – user3040301 Dec 04 '13 at 23:07
  • No problem, just make sure you understand the material. Copying code doesn't make you better. – Adam Smith Dec 04 '13 at 23:22
  • So the roll that gets the most numbers is supposed to be the one with 80 "#" characters. I've been trying to think about it & mess around with it for a while but for some reason I can't get the max to have 80 "#" & have it decrease from there... – user3040301 Dec 05 '13 at 05:19
  • So you want to set up an equivalent ratio between count/highest_count and num_#'s / 80. See higher up for how I did this in my answer. I won't edit again since my answer is technically complete, but it's important you understand how to apply ratios to programming problems -- it's pretty basic! Basically you want to find the ratio K where K*count = 80 for the largest count, and decreases from there. You can use `biggest_count = sum([roll_array[key] for key in roll_array])` to find the biggest count. Make sure you understand how that function works -- list comprehensions are stupidly useful. – Adam Smith Dec 05 '13 at 07:15
1

I personally would do something like this:

import random
from collections import defaultdict

def main():
    dice = int(input("Enter the number of dice: "))
    sides = int(input("Enter the number of sides: "))
    rolls = int(input("Enter the number of rolls to simulate: "))
    result = roll(dice, sides, rolls)
    maxH = 0
    for i in range(dice, dice * sides + 1):
        if result[i] / rolls > maxH: maxH = result[i] / rolls
    for i in range(dice, dice * sides + 1):
        print('{:2d}{:10d}{:8.2%} {}'.format(i, result[i], result[i] / rolls, '#' * int(result[i] / rolls / maxH * 40)))


def roll(dice, sides, rolls):
    d = defaultdict(int)
    for _ in range(rolls):
        d[sum(random.randint(1, sides) for _ in range(dice))] += 1
    return d

main()
Hyperboreus
  • 31,997
  • 9
  • 47
  • 87