钟形条件信号频谱的非均匀频率区间离散化算法优化及简洁实现方案问询
Got it, let's fix that clunky code—you want a way to generate frequency samples that cluster tightly around your peak f_max without all the manual band splits and extra parameters. Here's a clean, low-input approach using a sigmoid-based nonlinear mapping that works in both Python and MATLAB, and lets you control sample density with just a handful of key values.
Core Idea
Instead of splitting your frequency range into arbitrary bands, we use a sigmoid function to warp uniform [0,1] samples into your target frequency interval. This naturally clusters samples around f_max, and you can adjust a single "sharpness" parameter to control how tight that clustering is. No more defining separate sample counts for pre-peak, peak, and post-peak regions—just set a total number of samples.
Python Implementation
import numpy as np import matplotlib.pyplot as plt def generate_nonuniform_freq(f_start, f_end, f_max, total_samples, sharpness=5): # Normalize peak frequency to a [0,1] range relative to your bounds max_norm = (f_max - f_start) / (f_end - f_start) # Generate evenly spaced points between 0 and 1 x = np.linspace(0, 1, total_samples) # Sigmoid mapping to cluster points around the normalized peak # Higher sharpness = more samples concentrated near f_max sigmoid_x = 1 / (1 + np.exp(-sharpness * (x - max_norm))) # Map back to your original frequency range freq = f_start + (f_end - f_start) * sigmoid_x # Calculate frequency steps for downstream use df = np.diff(freq) return freq, df # Example usage (matches your original parameters) f_start = 0.016 f_end = 1.25 f_max = 0.16 total_samples = 50 # Total points you want (replace your N array) sharpness = 8 # Crank this up for tighter clustering around f_max freq, df = generate_nonuniform_freq(f_start, f_end, f_max, total_samples, sharpness) # Plot full range plt.figure(figsize=(10, 4)) plt.plot(freq, np.ones_like(freq), 'b-o', markersize=4, linewidth=1.5) plt.scatter([f_start, f_max, f_end], [1, 1, 1], c='red', s=60, zorder=5) plt.xlabel('Frequency (Hz)') plt.title('Non-Uniform Frequency Samples Clustered at Peak') plt.grid(True, alpha=0.5) plt.show() # Plot zoomed view near peak plt.figure(figsize=(10, 4)) plt.plot(freq, np.ones_like(freq), 'b-o', markersize=4, linewidth=1.5) plt.scatter([f_start, f_max, f_end], [1, 1, 1], c='red', s=60, zorder=5) plt.xlabel('Frequency (Hz)') plt.title('Zoomed View Near Peak') plt.grid(True, alpha=0.5) plt.xlim(0, 0.4) plt.show()
MATLAB Implementation
function [freq, df] = generate_nonuniform_freq(f_start, f_end, f_max, total_samples, sharpness) % Set default sharpness if not provided if nargin < 5 sharpness = 5; end % Normalize peak to [0,1] relative to frequency bounds max_norm = (f_max - f_start) / (f_end - f_start); % Generate uniform [0,1] samples x = linspace(0, 1, total_samples); % Sigmoid mapping to cluster samples around peak sigmoid_x = 1 ./ (1 + exp(-sharpness * (x - max_norm))); % Map back to original frequency range freq = f_start + (f_end - f_start) * sigmoid_x; % Calculate frequency steps df = diff(freq); end % Example usage f_start = 0.016; f_end = 1.25; f_max = 0.16; total_samples = 50; sharpness = 8; [freq, df] = generate_nonuniform_freq(f_start, f_end, f_max, total_samples, sharpness); % Full range plot figure('Position', [100, 100, 800, 320]) plot(freq, ones(size(freq)), 'b-o', 'MarkerSize', 4, 'LineWidth', 1.5) hold on scatter([f_start, f_max, f_end], [1,1,1], 60, 'r', 'filled', 'ZOrder', 5) xlabel('Frequency (Hz)') title('Non-Uniform Frequency Samples Clustered at Peak') grid on alpha(0.5) hold off % Zoomed peak plot figure('Position', [100, 100, 800, 320]) plot(freq, ones(size(freq)), 'b-o', 'MarkerSize', 4, 'LineWidth', 1.5) hold on scatter([f_start, f_max, f_end], [1,1,1], 60, 'r', 'filled', 'ZOrder', 5) xlabel('Frequency (Hz)') title('Zoomed View Near Peak') grid on alpha(0.5) xlim([0, 0.4]) hold off
Why This Works Better
- Minimal inputs: Only need
f_start,f_end,f_max, total samples, and optional sharpness. No more manual band splits or multiple sample count parameters. - Smooth transitions: Unlike your original code's hard band edges, samples shift gradually from sparse (far from peak) to dense (near peak), which aligns better with a bell-shaped spectrum's energy distribution.
- Flexible control: Adjust
sharpnessto fine-tune density:- Lower values (2-5): More spread-out samples, closer to uniform
- Higher values (8-15): Tighter clustering around
f_max
Alternative: Log-Based Clustering
If you need even sparser samples in the low/high frequency tails (common for some bell-shaped spectra), you can combine the sigmoid mapping with a log transformation. Here's a Python variant:
def generate_log_clustered_freq(f_start, f_end, f_max, total_samples, stretch=2): # Small shift to avoid log(0) issues shift = f_start * 1.1 f_start_shifted = f_start + shift f_end_shifted = f_end + shift f_max_shifted = f_max + shift # Normalize peak in log space max_log_norm = (np.log(f_max_shifted) - np.log(f_start_shifted)) / (np.log(f_end_shifted) - np.log(f_start_shifted)) x = np.linspace(0, 1, total_samples) # Sigmoid cluster around log-space peak sigmoid_x = 1 / (1 + np.exp(-stretch * (x - max_log_norm))) # Map back to linear frequency space log_freq = np.log(f_start_shifted) + sigmoid_x * (np.log(f_end_shifted) - np.log(f_start_shifted)) freq = np.exp(log_freq) - shift df = np.diff(freq) return freq, df
内容的提问来源于stack exchange,提问作者Varga

