Matrix of heights test
An old question, bet let's leave no question unanswered... I will go through your code (mostly) in order in which it is presented.

Asking for multiple is, in my opinion, in the wrong position, as it is separated by functions definitions from the rest of the main code. I'd move it after the functions definitions.

Closely related to that, asking the user to answer with C-like logic of 1 for "yes" or 0 for "no" is not pleasant, especially since Python doesn't share C's woes with strings. Btw, int(...) has no problem with extra spaces, so (r)stripping is not needed.

And, since we're becoming user-friendly here, let's allow users to make typos without crashes and other unexpected behaviour by asking them again if they give a nonsense answer. All together:

while True:
    answer = input("Would you like answer runs (y/n)? ").strip().lower()
    if answer in {"y", "n", "yes", "no"}:
        break
multiple = (answer in {"y", "yes"})


Creating weighted randoms can be done in a more concise and configurable way. I have chosen this:

weights = [0, 0, 0, 0.1, 0.5, 0.95, 1]
def weighted_random():
    # between 3 ft and 6 ft
    major = random.random()
    major = next(idx for idx,limit in enumerate(weights) if limit &gt; major)
    return major + random.random()


The weights list (could've been a tuple as well) contains limits. The major value (the integer part) of the height is the index of the first value bigger than the random that was picked. So, for example, 0.05 is between 0 and 0.1, meaning that we pick major = 3 because 3 is the index of 0.1.

Apart from keeping the code short, this makes it trivial to change the weights, including adding more weight categories, as long as we keep to the premise that these define the integer part. Otherwise, we could've used a dictionary or some similar approach.

We create heights twice in the program, so let's not copy/paste this non-trivial and important code. A function is better:

def make_heights(rows=30, cols=30):
    return [[weighted_random() for j in range(cols)] for i in range(rows)]


Printing is also best done using generators and joining the elements with the space character:

def output_heights(data):
    print("Men:")
    for row in data:
        print(" ".join("%.3f" % el for el in row))


Your _zip function confused me at first. There is a zip() in Python, but it does something far more reasonable for a function with such a name. Consider renaming this to "transpose".

Either way, I think it's best to handle "the shortest of the tall ones" and "the tallest of the short ones" the same way, with a single function for each of them:

def shortest_of_talls(data):
    return min(max(row) for row in data)

def tallest_of_shorts(data):
    return max(min(row[j] for row in data) for j,_ in enumerate(data[0]))


Here, I use enumerate to get (index, value) tuples, and I use a throwaway variable _ for values because I don't need them. There are a few situations when _ is not a good choice, but that's beyond the scope of this review.

Btw, notice that I use data. It's more descriptive than array (albeit, heights or people might have been a better choice).

Your main if looks like this: if not multiple: ... else: .... Does it not make more sense to do if multiple: ... else: ...? I changed it thus, so let's first review the "multiple" code.

I prefer evalf = list() over evalf = [], as I find it more readable. You're more likely to notice a bug if you put set() instead of list() than if you put {} instead of [].

We decided above to be more user friendly, so let's keep that up when asking about the number of runs. You wanted to ask the user to give you an integer, and default to 10000 if they give 0. Here is the code that takes an empty(ish) string (we allow spaces) which is treated as the default, or a positive integer. Any other response will repeat the question:

    while True:
        times = input("Number of evaluations (leave empty for 10000)? ")
        if times.strip() == "":
            times = 10000
            break
        try:
            times = int(times)
            if times &gt; 0:
                break
        except ValueError:
            pass


The next part of your code is biased toward what you know (expect?) is the right answer. If I got it right, this is a research question, so let's research the results properly, by counting how many "tallest of shorts", "shortest of talls", and "equals" are there:

    cnt_tos = cnt_sot = cnt_eq = 0


Since your default is to run 10000 tests, and that takes time (roughly a minute on my laptop), I decided to add progress information here. It's a neat little trick: print a message, but end with "\r" instead of "\n" to place the cursor at the beginning of the same line, instead of the next one:

    cnt_tos = cnt_sot = cnt_eq = 0
    done = -1
    for run in range(times):
        new_done = 100*run // times
        if new_done &gt; done:
            done = new_done
            print("Done: %d%%" % done, end="\r")
        heights = make_heights()

        tall_man = shortest_of_talls(heights)
        short_man = tallest_of_shorts(heights)

        if tall_man &gt; short_man:
            cnt_sot += 1
        elif tall_man &lt; short_man:
            cnt_tos += 1
        else:
            cnt_eq += 1

    print("Out of %d evaluations, we got:" % times)
    print("  * {times} times ({percent:.3f}%) the shortest of tall was taller,".format(times=cnt_sot, percent=100*cnt_sot/times))
    print("  * {times} times ({percent:.3f}%) the tallest or short was taller,".format(times=cnt_tos, percent=100*cnt_tos/times))
    print("  * {times} times ({percent:.3f}%) they were equal.".format(times=cnt_eq, percent=100*cnt_eq/times))


Notice that I have removed many blank lines that you have. The Style Guide for Python, which is one of the core documents for Python programmers, suggests that those should be used sparingly. I am probably too generous with them, but I prefer separating parts of the code that do semantically different parts of the job.

I also replaced C-style formats for the newer format() function, to make the code more readable (and, in theory, easier to change/translate, as these arguments are no longer positional). I will rarely use C-style formatting if there is more than one variable involved.

I did similar changes to your "single run" branch:

    heights = make_heights()
    output_heights(heights)

    tall_man = shortest_of_talls(heights)
    short_man = tallest_of_shorts(heights)

    print("Tallest of short:", short_man)
    print("Shortest of tall:", tall_man)
    print(
        "The answer is: ",
        "tallest of short" if short_man &gt; tall_man else "shortest of tall"
    )


As you can see, Python has ternary operator, and this is far more readable than the original print(["tallest of short", "shortest of tall"][short_man &lt; tall_man]).

Here is my variant of your code as a whole:

#!/usr/bin/env python3

import random

# Between 3 ft and 6 ft
weights = [0, 0, 0, 0.1, 0.5, 0.95, 1]

def weighted_random():
    major = random.random()
    major = next(idx for idx,limit in enumerate(weights) if limit &gt; major)
    return major + random.random()

def make_heights(rows=30, cols=30):
    return [[weighted_random() for j in range(cols)] for i in range(rows)]

def output_heights(data):
    print("Men:")
    for row in data:
        print(" ".join("%.3f" % el for el in row))

def shortest_of_talls(data):
    return min(max(row) for row in data)

def tallest_of_shorts(data):
    return max(min(row[j] for row in data) for j,_ in enumerate(data[0]))

while True:
    answer = input("Would you like answer runs (y/n)? ").strip().lower()
    if answer in {"y", "n", "yes", "no"}:
        break
multiple = (answer in {"y", "yes"})

if multiple:
    evalf = list()
    while True:
        times = input("Number of evaluations (leave empty for 10000)? ")
        if times.strip() == "":
            times = 10000
            break
        try:
            times = int(times)
            if times &gt; 0:
                break
        except ValueError:
            pass

    cnt_tos = cnt_sot = cnt_eq = 0
    done = -1
    for run in range(times):
        new_done = 100*run // times
        if new_done &gt; done:
            done = new_done
            print("Done: %d%%" % done, end="\r")
        heights = make_heights()

        tall_man = shortest_of_talls(heights)
        short_man = tallest_of_shorts(heights)

        if tall_man &gt; short_man:
            cnt_sot += 1
        elif tall_man &lt; short_man:
            cnt_tos += 1
        else:
            cnt_eq += 1

    print("Out of %d evaluations, we got:" % times)
    print("  * {times} times ({percent:.3f}%) the shortest of tall was taller,".format(times=cnt_sot, percent=100*cnt_sot/times))
    print("  * {times} times ({percent:.3f}%) the tallest or short was taller,".format(times=cnt_tos, percent=100*cnt_tos/times))
    print("  * {times} times ({percent:.3f}%) they were equal.".format(times=cnt_eq, percent=100*cnt_eq/times))
else:
    heights = make_heights()

    output_heights(heights)

    tall_man = shortest_of_talls(heights)
    short_man = tallest_of_shorts(heights)

    print("Tallest of short:", short_man)
    print("Shortest of tall:", tall_man)
    print(
        "The answer is: ",
        "tallest of short" if short_man &gt; tall_man else "shortest of tall"
    )


Apart from these, I have three more comments:


Use sensible names. Apart from zip and array, there was also zeroes, which was probably meant as "those where the condition is zero (it's actually False in Python)", but this is really not informative to anyone reading the code.
Consider including docstrings in all of your functions. It's a good habit to have, lest you end up banging your head on the desk when reading trying to understand your own code in a few months.
Since you're working with matrices, using NumPy's array could be a good choice, especially because it provides a simple and fast transpose and weighted sampling.