nth Factorial of n 1s in a row In what way can I improve this? Be that performance, readability, etc.
I'm thinking implementing NumPy would improve performance.
Usage: Calculate* the nth factorial of n 1s in a row.
*No need for a precise calculation. Magnitude and leading digits is enough.
Examples:
2nd factorial of 11. (11 x 9 x 7 x 5 x 3 x 1)
3rd factorial of 111. (111 x 108 x 105....)
...
import multiprocessing
import argparse
import datetime
import math

parser = argparse.ArgumentParser(
    formatter_class=argparse.HelpFormatter,
    description=&quot;Calcs x factorial&quot;,
    usage=&quot;&quot;
)

parser.add_argument(&quot;-n&quot;, &quot;--number&quot;, type=int)

args = parser.parse_args()

def first_n_digits(num, n):
    return num // 10 ** (int(math.log(num, 10)) - n + 1)

def getones():
    if args.number == 1 :
        print(1)
        exit()

    num = &quot;&quot;
    for _ in range(0, args.number) :
        num += &quot;&quot;.join(&quot;1&quot;)
    return int(num)

def getlog(send_end, i, threads, num):
    inc = int(num/threads)
    inc -= int(inc%args.number)
    start = num-inc*i
    
    end = num-inc*(i+1) if i &lt; threads-1 else 0

    output = 0
    for j in range(start, end, -args.number):
        if j &gt; 0:
            output += math.log10(j)
    
    send_end.send(output)

def main():
    num = getones()
    threads = multiprocessing.cpu_count() if getones()/multiprocessing.cpu_count() &gt; multiprocessing.cpu_count() else 1
    jobs = []
    pipe_list = []
    for i in range(threads):
        recv_end, send_end = multiprocessing.Pipe(False)
        p = multiprocessing.Process(target=getlog, args=(send_end, i, threads, num))
        jobs.append(p)
        pipe_list.append(recv_end)
        p.start()
    i = 0
    for proc in jobs:
        proc.join()
        i+=1
    result_list = [output.recv() for output in pipe_list]

    magnitude = int(sum(result_list))
    normalized = 10**(sum(result_list)-magnitude)
    
    fnd = str(first_n_digits(normalized, 3))
    print(&quot;{}.{}{}e{}&quot;.format(fnd[0], fnd[1], fnd[2], magnitude))
    
if __name__ == '__main__':
    start = datetime.datetime.now()
    main()
    end = datetime.datetime.now()
    print(end-start)
```
One thing I'd change is to not use args everywhere. You use the name &quot;n&quot; in the problem description, so I'd use that in the program as well. Reading args only once, to store the value in n.
Your getones function is rather lengthy and inefficient, taking O(n2) time to build the string. Although the rest of the program can't handle large n anyway, so efficiency doesn't really matter here. (And maybe the conversion to int takes quadratic time, not sure.)
Here's a shorter solution:
e = sum(map(log10, range(int('1' * n), 0, -n)))
print('%.2fe%d' % (10**(e % 1), e // 1))

Demo:
&gt;&gt;&gt; n = 9
&gt;&gt;&gt; from math import log10
&gt;&gt;&gt; e = sum(map(log10, range(int('1' * n), 0, -n)))
&gt;&gt;&gt; print('%.2fe%d' % (10**(e % 1), e // 1))
9.22e93968682

Comparison with yours (at repl.it) for n=9, same result and mine's about three times as fast:
n = 9
yours:
9.22e93968682
0:00:11.911943
mine:
9.22e93968682
0:00:03.964518

We have a different result for example for n=2:
n = 2
yours:
1.03e4
0:00:00.011884
mine:
1.04e4
0:00:00.000085

The exact result is:
&gt;&gt;&gt; math.prod(range(11, 0, -2))
10395

So I'd say yours isn't rounding correctly.