Skip to content

Commit 01f0b1c

Browse files
Option to skip trial division
1 parent fcd134b commit 01f0b1c

File tree

6 files changed

+40
-29
lines changed

6 files changed

+40
-29
lines changed

FindAFactor/_find_a_factor.cpp

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,8 @@ struct Factorizer {
10081008
};
10091009

10101010
std::string find_a_factor(std::string toFactorStr, size_t method, size_t nodeCount, size_t nodeId, size_t trialDivisionLevel, size_t gearFactorizationLevel,
1011-
size_t wheelFactorizationLevel, double smoothnessBoundMultiplier, double batchSizeMultiplier, size_t batchSizeVariance, size_t ladderMultiple) {
1011+
size_t wheelFactorizationLevel, double smoothnessBoundMultiplier, double batchSizeMultiplier, size_t batchSizeVariance, size_t ladderMultiple,
1012+
bool skipTrialDivision) {
10121013
// Validation section
10131014
if (method > 1U) {
10141015
std::cout << "Mode number " << method << " not implemented. Defaulting to FACTOR_FINDER." << std::endl;
@@ -1048,26 +1049,28 @@ std::string find_a_factor(std::string toFactorStr, size_t method, size_t nodeCou
10481049
const auto itg = std::upper_bound(primes.begin(), primes.end(), gearFactorizationLevel);
10491050
const size_t wgDiff = std::distance(itw, itg);
10501051

1051-
// This is simply trial division up to the ceiling.
1052-
std::mutex trialDivisionMutex;
1053-
for (size_t primeIndex = 0U; (primeIndex < primes.size()) && (result == 1U); primeIndex += 64U) {
1054-
dispatch.dispatch([&toFactor, &primes, &result, &trialDivisionMutex, primeIndex]() -> bool {
1055-
const size_t maxLcv = std::min(primeIndex + 64U, primes.size());
1056-
for (size_t pi = primeIndex; pi < maxLcv; ++pi) {
1057-
const size_t& currentPrime = primes[pi];
1058-
if (!(toFactor % currentPrime)) {
1059-
std::lock_guard<std::mutex> lock(trialDivisionMutex);
1060-
result = currentPrime;
1061-
return true;
1052+
if (!skipTrialDivision) {
1053+
// This is simply trial division up to the ceiling.
1054+
std::mutex trialDivisionMutex;
1055+
for (size_t primeIndex = 0U; (primeIndex < primes.size()) && (result == 1U); primeIndex += 64U) {
1056+
dispatch.dispatch([&toFactor, &primes, &result, &trialDivisionMutex, primeIndex]() -> bool {
1057+
const size_t maxLcv = std::min(primeIndex + 64U, primes.size());
1058+
for (size_t pi = primeIndex; pi < maxLcv; ++pi) {
1059+
const size_t& currentPrime = primes[pi];
1060+
if (!(toFactor % currentPrime)) {
1061+
std::lock_guard<std::mutex> lock(trialDivisionMutex);
1062+
result = currentPrime;
1063+
return true;
1064+
}
10621065
}
1063-
}
1064-
return false;
1065-
});
1066-
}
1067-
dispatch.finish();
1068-
// If we've checked all primes below the square root of toFactor, then it's prime.
1069-
if ((result != 1U) || (toFactor <= (primeCeiling * primeCeiling))) {
1070-
return boost::lexical_cast<std::string>(result);
1066+
return false;
1067+
});
1068+
}
1069+
dispatch.finish();
1070+
// If we've checked all primes below the square root of toFactor, then it's prime.
1071+
if ((result != 1U) || (toFactor <= (primeCeiling * primeCeiling))) {
1072+
return boost::lexical_cast<std::string>(result);
1073+
}
10711074
}
10721075

10731076
// Set up wheel factorization (or "gear" factorization)

FindAFactor/find_a_factor.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ def find_a_factor(n,
1919
smoothness_bound_multiplier=float(os.environ.get('FINDAFACTOR_SMOOTHNESS_BOUND_MULTIPLIER')) if os.environ.get('FINDAFACTOR_SMOOTHNESS_BOUND_MULTIPLIER') else 1.0,
2020
batch_size_multiplier=float(os.environ.get('FINDAFACTOR_BATCH_SIZE_MULTIPLIER')) if os.environ.get('FINDAFACTOR_BATCH_SIZE_MULTIPLIER') else 512.0,
2121
batch_size_variance=int(os.environ.get('FINDAFACTOR_BATCH_SIZE_VARIANCE')) if os.environ.get('FINDAFACTOR_BATCH_SIZE_VARIANCE') else 2,
22-
ladder_multiple=int(os.environ.get('FINDAFACTOR_LADDER_MULTIPLE')) if os.environ.get('FINDAFACTOR_LADDER_MULTIPLE') else 6):
22+
ladder_multiple=int(os.environ.get('FINDAFACTOR_LADDER_MULTIPLE')) if os.environ.get('FINDAFACTOR_LADDER_MULTIPLE') else 6,
23+
skip_trial_division=True if os.environ.get('FINDAFACTOR_SKIP_TRIAL_DIVISION') else False):
2324
return int(_find_a_factor._find_a_factor(str(n),
2425
int(method),
2526
node_count, node_id,
@@ -29,4 +30,5 @@ def find_a_factor(n,
2930
smoothness_bound_multiplier,
3031
batch_size_multiplier,
3132
batch_size_variance,
32-
ladder_multiple))
33+
ladder_multiple,
34+
skip_trial_division))

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ factor = find_a_factor(
3535
smoothness_bound_multiplier=1.0,
3636
batch_size_multiplier=512.0,
3737
batch_size_variance=2,
38-
ladder_multiple=5
38+
ladder_multiple=5,
39+
skip_trial_division=False
3940
)
4041
```
4142

@@ -51,6 +52,7 @@ The `find_a_factor()` function should return any nontrivial factor of `to_factor
5152
- `batch_size_multiplier` (default value: `512.0`): For `FACTOR_FINDER`/`1` method, each `1.0` increment of the multiplier adds `k ln k` Markov mixing replacement steps for `k` count of smooth primes, before reseeding Monte Carlo.
5253
- `batch_size_variance` (default value: `2`): For `FACTOR_FINDER`/`1` method, `k ln k` is the right proportionality for a Markov mixing process, but a linear factor in front is hard to predict. As such, it can be useful to dynamically vary the batch size, as if to cover and amortize the cost of several different batch sizes at once. In sequence, each batch size will be multiplied by `2 ** i` for `i` in `range(batch_size_variance)`, repeating from `0`.
5354
- `ladder_multiple` (default value: `6`): Controls how many times randomly-selected square prime multiplication is repeated with the same square prime per random selection, in ascending a "ladder" of smooth perfect squares. A random number between `1` and `ladder_multiple` is selected for how many times the current smooth perfect square is multiplied each randomly selected square prime, while division still occurs 1 square prime multiple at a time. (Any smooth perfect square can be multiplied by any square prime in the factor base, or any other smooth perfect square, and produce a different smooth perfect square.)
55+
- `skip_trial_division` (default value: `False`): `False` performs initial-phase trial division, and `True` skips it. Note that `trial_division_level` still functions as the input to Sieve of Eratosthenes to collect a list of primes from which (`FACTOR_FINDER`) smooth primes can be selected. If `skip_trial_division=True`, simply set `trial_division_level` high enough to avoid truncation warnings, unless otherwise desired.
5456

5557
All variables defaults can also be controlled by environment variables:
5658
- `FINDAFACTOR_METHOD` (integer value)

find_a_factor

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ def main():
2020
trial_division_level=int(os.environ.get('FINDAFACTOR_TRIAL_DIVISION_LEVEL')) if os.environ.get('FINDAFACTOR_TRIAL_DIVISION_LEVEL') else (1<<20)
2121
gear_factorization_level = int(os.environ.get('FINDAFACTOR_GEAR_FACTORIZATION_LEVEL')) if os.environ.get('FINDAFACTOR_GEAR_FACTORIZATION_LEVEL') else 11
2222
wheel_factorization_level = int(os.environ.get('FINDAFACTOR_WHEEL_FACTORIZATION_LEVEL')) if os.environ.get('FINDAFACTOR_WHEEL_FACTORIZATION_LEVEL') else 11
23-
smoothness_bound_multiplier = float(os.environ.get('FINDAFACTOR_SMOOTHNESS_BOUND_MULTIPLIER')) if os.environ.get('FINDAFACTOR_SMOOTHNESS_BOUND_MULTIPLIER') else 1.0
24-
batch_size_multiplier=float(os.environ.get('FINDAFACTOR_BATCH_SIZE_MULTIPLIER')) if os.environ.get('FINDAFACTOR_BATCH_SIZE_MULTIPLIER') else 512.0
23+
smoothness_bound_multiplier = float(os.environ.get('FINDAFACTOR_SMOOTHNESS_BOUND_MULTIPLIER')) if os.environ.get('FINDAFACTOR_SMOOTHNESS_BOUND_MULTIPLIER') else 2.0
24+
batch_size_multiplier=float(os.environ.get('FINDAFACTOR_BATCH_SIZE_MULTIPLIER')) if os.environ.get('FINDAFACTOR_BATCH_SIZE_MULTIPLIER') else 256.0
2525
batch_size_variance=int(os.environ.get('FINDAFACTOR_BATCH_SIZE_VARIANCE')) if os.environ.get('FINDAFACTOR_BATCH_SIZE_VARIANCE') else 2
2626
ladder_multiple=int(os.environ.get('FINDAFACTOR_LADDER_MULTIPLE')) if os.environ.get('FINDAFACTOR_LADDER_MULTIPLE') else 6
27+
skip_trial_division=True if os.environ.get('FINDAFACTOR_SKIP_TRIAL_DIVISION') else False
2728

2829
if argv_len > 2:
2930
method = FactoringMethod(int(sys.argv[2]))
@@ -43,7 +44,9 @@ def main():
4344
if argv_len > 10:
4445
ladder_multiple = int(sys.argv[10])
4546
if argv_len > 11:
46-
trial_division_level = int(sys.argv[11])
47+
skip_trial_division = sys.argv[11] not in ['False', '0']
48+
if argv_len > 12:
49+
trial_division_level = int(sys.argv[12])
4750

4851
start = time.perf_counter()
4952
result = find_a_factor(
@@ -57,7 +60,8 @@ def main():
5760
smoothness_bound_multiplier = smoothness_bound_multiplier,
5861
batch_size_multiplier = batch_size_multiplier,
5962
batch_size_variance = batch_size_variance,
60-
ladder_multiple=ladder_multiple
63+
ladder_multiple=ladder_multiple,
64+
skip_trial_division=skip_trial_division
6165
)
6266
print(time.perf_counter() - start)
6367
print(str(result) + " * " + str(to_factor // result) + " == " + str(to_factor))

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
88

99
[project]
1010
name = "FindAFactor"
11-
version = "4.7.11"
11+
version = "4.8.0"
1212
requires-python = ">=3.8"
1313
description = "Find any nontrivial factor of a number"
1414
readme = {file = "README.txt", content-type = "text/markdown"}

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def build_extension(self, ext):
4040

4141
setup(
4242
name='FindAFactor',
43-
version='4.7.11',
43+
version='4.8.0',
4444
author='Dan Strano',
4545
author_email='stranoj@gmail.com',
4646
description='Find any nontrivial factor of a number',

0 commit comments

Comments
 (0)