|
| 1 | +import math |
| 2 | + |
1 | 3 | print("=" * 58) |
2 | | -print("AP / GP / AGP / HP RECOGNIZER") |
| 4 | +print("📊 AP / GP / AGP / HP RECOGNIZER 📊") |
3 | 5 | print("=" * 58) |
4 | | -print("Enter at least 4 numbers separated by commas.") |
5 | | -print("Example: 2, 4, 6, 8 or 3, 6, 12, 24") |
| 6 | +print("Enter at least 4 numbers separated by commas to recognize the progression.") |
| 7 | +print("Example: 2, 4, 6, 8 or 3, 6, 12, 24\n") |
6 | 8 |
|
7 | 9 | EPS = 1e-9 |
8 | 10 |
|
| 11 | +while True: |
| 12 | + print("=" * 58) |
| 13 | + print("Choose an option:") |
| 14 | + print("1️⃣ Recognize sequence type") |
| 15 | + print("2️⃣ Exit") |
| 16 | + |
| 17 | + choice = input("🎯 Enter your choice (1-2): ").strip() |
9 | 18 |
|
10 | | -def is_close(a, b, eps=EPS): |
11 | | - return abs(a - b) <= eps |
| 19 | + if choice == "2": |
| 20 | + print("\n👋 Thanks for using the recognizer. Keep practicing! ✨\n") |
| 21 | + break |
12 | 22 |
|
| 23 | + if choice != "1": |
| 24 | + print("❌ Please choose 1 or 2.") |
| 25 | + continue |
13 | 26 |
|
14 | | -def parse_sequence(raw): |
15 | | - parts = [item.strip() for item in raw.split(",") if item.strip()] |
| 27 | + user_input = input("\n📝 Enter sequence values separated by commas: ").strip() |
| 28 | + if not user_input: |
| 29 | + print("❌ Error: Input cannot be empty.") |
| 30 | + continue |
| 31 | + |
| 32 | + # Parse sequence |
| 33 | + parts = [item.strip() for item in user_input.split(",") if item.strip()] |
16 | 34 | if len(parts) < 4: |
17 | | - return None, "Please enter at least 4 values." |
| 35 | + print("❌ Please enter at least 4 values.") |
| 36 | + continue |
18 | 37 |
|
19 | 38 | sequence = [] |
| 39 | + parse_error = False |
20 | 40 | for part in parts: |
21 | 41 | try: |
22 | 42 | sequence.append(float(part)) |
23 | 43 | except ValueError: |
24 | | - return None, f"Invalid number: {part}" |
25 | | - |
26 | | - return sequence, "" |
27 | | - |
28 | | - |
29 | | -def check_ap(sequence): |
30 | | - diff = sequence[1] - sequence[0] |
31 | | - for i in range(2, len(sequence)): |
32 | | - if not is_close(sequence[i] - sequence[i - 1], diff): |
33 | | - return False, None |
34 | | - return True, diff |
35 | | - |
| 44 | + print(f"❌ Invalid number: {part}") |
| 45 | + parse_error = True |
| 46 | + break |
36 | 47 |
|
37 | | -def check_gp(sequence): |
38 | | - if all(is_close(value, 0.0) for value in sequence): |
39 | | - return True, 0.0 |
| 48 | + if parse_error: |
| 49 | + continue |
40 | 50 |
|
41 | | - if any(is_close(sequence[i - 1], 0.0) for i in range(1, len(sequence))): |
42 | | - return False, None |
| 51 | + matched_types = [] |
43 | 52 |
|
44 | | - ratio = sequence[1] / sequence[0] |
| 53 | + # 1. Check AP (Arithmetic Progression) |
| 54 | + is_ap = True |
| 55 | + ap_diff = sequence[1] - sequence[0] |
45 | 56 | for i in range(2, len(sequence)): |
46 | | - if not is_close(sequence[i] / sequence[i - 1], ratio): |
47 | | - return False, None |
48 | | - |
49 | | - return True, ratio |
50 | | - |
51 | | - |
52 | | -def check_hp(sequence): |
53 | | - if any(is_close(value, 0.0) for value in sequence): |
54 | | - return False, None |
| 57 | + if abs((sequence[i] - sequence[i - 1]) - ap_diff) > EPS: |
| 58 | + is_ap = False |
| 59 | + break |
| 60 | + |
| 61 | + if is_ap: |
| 62 | + # Format helper |
| 63 | + val = ap_diff |
| 64 | + ap_diff_str = str(int(round(val))) if abs(val - round(val)) < 1e-9 else f"{val:.6g}" |
| 65 | + matched_types.append(f"AP (common difference d = {ap_diff_str})") |
| 66 | + |
| 67 | + # 2. Check GP (Geometric Progression) |
| 68 | + is_gp = True |
| 69 | + gp_ratio = 0.0 |
| 70 | + if all(abs(x) <= EPS for x in sequence): |
| 71 | + gp_ratio = 0.0 |
| 72 | + elif any(abs(sequence[i - 1]) <= EPS for i in range(1, len(sequence))): |
| 73 | + is_gp = False |
| 74 | + else: |
| 75 | + gp_ratio = sequence[1] / sequence[0] |
| 76 | + for i in range(2, len(sequence)): |
| 77 | + if abs((sequence[i] / sequence[i - 1]) - gp_ratio) > EPS: |
| 78 | + is_gp = False |
| 79 | + break |
55 | 80 |
|
56 | | - reciprocal = [1 / value for value in sequence] |
57 | | - is_ap, reciprocal_diff = check_ap(reciprocal) |
58 | | - if not is_ap: |
59 | | - return False, None |
| 81 | + if is_gp: |
| 82 | + val = gp_ratio |
| 83 | + gp_ratio_str = str(int(round(val))) if abs(val - round(val)) < 1e-9 else f"{val:.6g}" |
| 84 | + matched_types.append(f"GP (common ratio r = {gp_ratio_str})") |
60 | 85 |
|
61 | | - return True, reciprocal_diff |
| 86 | + # 3. Check HP (Harmonic Progression) |
| 87 | + is_hp = True |
| 88 | + hp_diff = 0.0 |
| 89 | + if any(abs(x) <= EPS for x in sequence): |
| 90 | + is_hp = False |
| 91 | + else: |
| 92 | + reciprocal = [1.0 / x for x in sequence] |
| 93 | + hp_diff = reciprocal[1] - reciprocal[0] |
| 94 | + for i in range(2, len(reciprocal)): |
| 95 | + if abs((reciprocal[i] - reciprocal[i - 1]) - hp_diff) > EPS: |
| 96 | + is_hp = False |
| 97 | + break |
62 | 98 |
|
| 99 | + if is_hp: |
| 100 | + val = hp_diff |
| 101 | + hp_diff_str = str(int(round(val))) if abs(val - round(val)) < 1e-9 else f"{val:.6g}" |
| 102 | + matched_types.append(f"HP (reciprocal AP difference = {hp_diff_str})") |
63 | 103 |
|
64 | | -def agp_candidates(sequence): |
| 104 | + # 4. Check AGP (Arithmetico-Geometric Progression) |
| 105 | + is_agp = False |
| 106 | + agp_ratio = 0.0 |
| 107 | + |
| 108 | + # Generate AGP ratio candidates |
65 | 109 | s0, s1, s2 = sequence[0], sequence[1], sequence[2] |
66 | | - |
67 | | - if is_close(s0, 0.0): |
68 | | - if is_close(s1, 0.0): |
69 | | - return [] |
70 | | - return [s2 / (2 * s1)] |
71 | | - |
72 | | - a = s0 |
73 | | - b = -2 * s1 |
74 | | - c = s2 |
75 | | - disc = b * b - 4 * a * c |
76 | | - |
77 | | - if disc < -EPS: |
78 | | - return [] |
79 | | - |
80 | | - if is_close(disc, 0.0): |
81 | | - return [-b / (2 * a)] |
82 | | - |
83 | | - if disc < 0: |
84 | | - return [] |
85 | | - |
86 | | - sqrt_disc = disc ** 0.5 |
87 | | - r1 = (-b + sqrt_disc) / (2 * a) |
88 | | - r2 = (-b - sqrt_disc) / (2 * a) |
89 | | - |
90 | | - if is_close(r1, r2): |
91 | | - return [r1] |
92 | | - |
93 | | - return [r1, r2] |
94 | | - |
95 | | - |
96 | | -def check_agp(sequence): |
97 | | - for ratio in agp_candidates(sequence): |
98 | | - a = sequence[0] |
99 | | - if is_close(ratio, 0.0): |
| 110 | + candidates = [] |
| 111 | + if abs(s0) <= EPS: |
| 112 | + if abs(s1) > EPS: |
| 113 | + candidates.append(s2 / (2 * s1)) |
| 114 | + else: |
| 115 | + a = s0 |
| 116 | + b = -2 * s1 |
| 117 | + c = s2 |
| 118 | + disc = b * b - 4 * a * c |
| 119 | + if disc >= -EPS: |
| 120 | + if abs(disc) <= EPS: |
| 121 | + candidates.append(-b / (2 * a)) |
| 122 | + else: |
| 123 | + sqrt_disc = math.sqrt(disc) |
| 124 | + candidates.append((-b + sqrt_disc) / (2 * a)) |
| 125 | + candidates.append((-b - sqrt_disc) / (2 * a)) |
| 126 | + |
| 127 | + for r in candidates: |
| 128 | + if abs(r) <= EPS: |
100 | 129 | # If common ratio r = 0, all terms from index 1 onwards must be 0 |
101 | | - if all(is_close(x, 0.0) for x in sequence[1:]): |
102 | | - return True, ratio |
| 130 | + if all(abs(x) <= EPS for x in sequence[1:]): |
| 131 | + is_agp = True |
| 132 | + agp_ratio = r |
| 133 | + break |
103 | 134 | continue |
104 | 135 |
|
105 | | - d = (sequence[1] / ratio) - a |
106 | 136 | valid = True |
107 | | - for i in range(len(sequence)): |
108 | | - expected = (a + i * d) * (ratio ** i) |
109 | | - if not is_close(sequence[i], expected, eps=1e-7): |
| 137 | + for i in range(2, len(sequence)): |
| 138 | + expected = 2 * r * sequence[i - 1] - (r * r) * sequence[i - 2] |
| 139 | + if abs(sequence[i] - expected) > 1e-7: |
110 | 140 | valid = False |
111 | 141 | break |
112 | 142 | if valid: |
113 | | - return True, ratio |
114 | | - |
115 | | - return False, None |
116 | | - |
117 | | - |
118 | | -def format_number(value): |
119 | | - if is_close(value, round(value)): |
120 | | - return str(int(round(value))) |
121 | | - return f"{value:.6g}" |
122 | | - |
123 | | - |
124 | | -while True: |
125 | | - print("\nChoose an option:") |
126 | | - print("1. Recognize sequence type") |
127 | | - print("2. Exit") |
128 | | - |
129 | | - choice = input("Enter your choice (1-2): ").strip() |
130 | | - |
131 | | - if choice == "2": |
132 | | - print("\nThanks for using the recognizer. Keep practicing! ✨") |
133 | | - break |
134 | | - |
135 | | - if choice != "1": |
136 | | - print("Please choose 1 or 2.") |
137 | | - continue |
138 | | - |
139 | | - user_input = input("\nEnter sequence values separated by commas: ") |
140 | | - sequence, error = parse_sequence(user_input) |
141 | | - |
142 | | - if error: |
143 | | - print(f"❌ {error}") |
144 | | - continue |
145 | | - |
146 | | - matched_types = [] |
147 | | - |
148 | | - ap_ok, ap_diff = check_ap(sequence) |
149 | | - if ap_ok: |
150 | | - matched_types.append(f"AP (common difference d = {format_number(ap_diff)})") |
151 | | - |
152 | | - gp_ok, gp_ratio = check_gp(sequence) |
153 | | - if gp_ok: |
154 | | - matched_types.append(f"GP (common ratio r = {format_number(gp_ratio)})") |
155 | | - |
156 | | - agp_ok, agp_ratio = check_agp(sequence) |
157 | | - if agp_ok: |
158 | | - matched_types.append(f"AGP (repetition ratio r = {format_number(agp_ratio)})") |
| 143 | + is_agp = True |
| 144 | + agp_ratio = r |
| 145 | + break |
159 | 146 |
|
160 | | - hp_ok, hp_diff = check_hp(sequence) |
161 | | - if hp_ok: |
162 | | - matched_types.append( |
163 | | - f"HP (reciprocal AP difference = {format_number(hp_diff)})" |
164 | | - ) |
| 147 | + if is_agp: |
| 148 | + val = agp_ratio |
| 149 | + agp_ratio_str = str(int(round(val))) if abs(val - round(val)) < 1e-9 else f"{val:.6g}" |
| 150 | + matched_types.append(f"AGP (repetition ratio r = {agp_ratio_str})") |
165 | 151 |
|
166 | | - print("\nResult") |
| 152 | + # Display Results |
| 153 | + print("\n💡 Result") |
167 | 154 | print("-" * 58) |
168 | | - print("Sequence:", ", ".join(format_number(x) for x in sequence)) |
| 155 | + formatted_seq = [] |
| 156 | + for x in sequence: |
| 157 | + f_str = str(int(round(x))) if abs(x - round(x)) < 1e-9 else f"{x:.6g}" |
| 158 | + formatted_seq.append(f_str) |
| 159 | + print("Sequence:", ", ".join(formatted_seq)) |
169 | 160 |
|
170 | 161 | if matched_types: |
171 | 162 | print("✅ Recognized as:") |
|
0 commit comments