Question

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% 
Was it helpful?

Solution

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()

OTHER TIPS

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 :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top